具有细节浏览功能的图片浏览器
前言
有网购经历的小伙伴想必对下面的情景都不陌生:当鼠标在商品图片上滑动时,下方会有一个小区域展示商品的细节。“前端”技术可以做出这个效果,但android也不是省油的,下面就带大家手工制作一个具有上述功能的app(功能更强大,还可以改变图片的透明度)。
基本知识
以下是本次开发涉及到的知识
View组件之一ImageView
ImageView的主要功能是用来显示包括图片在内的所有Drawable对象。
主要属性有:
android:scaleType //设置图片如何缩放及移动以适应ImageView的大小
对应属性值:
android:scaleType="fitXY" //图片纵向和横向独立缩放直至该图片完全适应该ImageView的大小
android:scaleType="fitCenter //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在ImageView中央
android:scaleType="fitStart //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在ImageView左上角
android:scaleType="fitEnd //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在右上角
android:scaleType="center //把图片放置在ImageView中央,不进行任何缩放
另一个常用的属性值
android:src//指定ImageView显示哪张图片
如
android:src="@drawable/ic_launcher" //该ImageView显示drawable中名称为ic_launcher的图片
ImageView常用方法:
setImageBitmap(Bitmap)//当要显示的是位图Bitmap对象时调用此方法
setImageDrawable(Drawable)//当要显示的是Drawable对象时调用此方法
setImageResource(int)//使用图片的ID设置该ImageView显示的图片(在我的文章《使用命令行徒手开发安卓项目》中,我已说过R.id、R.id.drawable对应对象为int类型,因此读者不应对此方法的形参类型感到疑惑)
setImageAlpha(int alpha)//设置图片的透明度,透明度定义为int型
getDrawable()//从ImageView中获取包装有位图的BitmapDrawable对象
上面前三个方法作用和android:src是一样的,区别在于android:src用于xml布局文件,而这些方法用于java文件中。当需要图片随着用户的动作而变化时,就需要用到这些方法了,android:src适用于图片恒定不变的情况或者用于设置第一张图片。
第一个和第三个方法在实战中运用到,读者可参考加深印象
Bitmap对象和BitmapDrawable对象
Bitmap代表位图,BitmapDrawable对象用于封装一个Bitmap对象,它们之间的转化如下:
BitmapDrawable bitmapdrawable=new BitmapDrawable(bitmap)//BitmapDrawable的单参构造器,参数为Bitmap的一个对象
Bitmap bitmap=bitmapdrawable.getBitmap();//获取BitmapDrawable对象中所包装的Bitmap对象
要获取Bitmap对象,除了可以用上面的方法,还有以下常用方法:
createBitmap(Bitmap source, int x,int y, int width, int height)//在源位图上从坐标为(x,y)的点开始抠出宽为width,高为height的图片,保存为新的位图
想一想photoshop中的抠图对这个方法会有很好的理解,在我们制作app的过程中,我们正是使用这个方法显示一张图片的细节部分的。
事件监听器
何为事件监听器,简单来说,我们为一些组件(这些组件称为事件源)添加事件监听器后,当出现事件时(如单击按钮,触摸屏幕等),事件监听器就会调用内部的事件处理器(通常为各种自定义方法)来进行响应。
总结来说,有三要素:事件源、事件处理器,事件,我们必须三个元素都给予明确,下面详细说明如何明确。
明确事件处理器:
本次开发我们使用到抽象类View.OnClickListener和View.OnTouchListener,它们的含义通过名称不难看出,在此不赘述。既然是抽象类,我们在创建它的一个对象之前,必须先改写类内部的onClick和onTouch成员方法,这其实就是在明确事件处理器。示例如下:
View.OnClickListener listener=new View.OnClickListener()//创建OnClickListener的一个对象listener
{
public void onClick(View view) //重写onClick函数
{
show.setText("请关注国之利刃2013"); //事件处理器定义:单击后就在id为show的文本框中显示”请关注国之利刃2013"
}
};
指定事件源:
指定事件源比较简单,只要调用组件自身提供的方法就行了。常用的方法有setOnClickListener(OnClickListener)和setOnTouchListener(OnTouchListener),set可以理解为"注册"的意思,这些方法就是为组件注册事件监听器。在我们调用这些方法时,注册组件的id就会传给onClick或onTouch函数的形参view(理解这一点对理解接下来开发应用的代码很有帮助)
明确事件:这个很简单,我们的OnClickListener和OnTouchListener就指定事件是单击还是触摸屏幕。
实战
有了以上储备知识,我们就可以着手具有细节浏览功能的图片浏览器的app的开发了。我们分xml布局文件和业务实现java文件两部分进行讲解。
xml文件中定义了四个按钮,两个单击时会切换图片,两个单击时用于改变图片透明度
除此之外,还定义了两个ImageView,一个用于展示完整图片,当触摸时会将触摸点附近的部分图片显示在另一个ImageView中,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@color/colorAccent"//为了美观,把背景色设为粉红色
tools:context=".PictureBrowser">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/next"
android:text="下一张" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/next"//设置位置
android:layout_marginLeft="50pt"//设置左留白
android:id="@+id/last"
android:text="上一张" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/next"//设置位置
android:layout_alignLeft="@id/next"//设置该按钮与id为next的组件左对齐
android:id="@+id/improve"
android:text="提高透明度" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/improve"//设置位置
android:layout_alignTop="@id/improve"//顶部对齐
android:layout_alignLeft="@id/last"//左对齐
android:id="@+id/lower"
android:text="降低透明度" />
<ImageView
android:layout_width="110pt"//设定ImageView的大小
android:layout_height="100pt"
android:layout_below="@id/lower"//位置
android:layout_alignLeft="@id/next"//左对齐
android:id="@+id/image1"
android:scaleType="fitCenter"/>//设置图片放缩及移动的方式
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/image1"//位置
android:layout_alignLeft="@id/image1"//左对齐
android:layout_marginLeft="20pt"//左留白
android:id="@+id/image2" />
</RelativeLayout>
java文件主要就是为各组件添加事件监听器,代码如下:
package com.golfer.www.picturebrowser;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class PictureBrowser extends AppCompatActivity {
int[] photo=new int[]//定义图片数组,元素都是drawable里面的图片资源
{
R.drawable.wedding2,
R.drawable.wedding1,
R.drawable.wedding3,
R.drawable.wedding4,
R.drawable.wedding5,
R.drawable.wedding6,
R.drawable.wedding7,
R.drawable.wedding8,
};
Button next;
Button last;
Button improve;
Button lower;
ImageView img1;
ImageView img2;
int current=0;
int alpha=255;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
next=(Button)findViewById(R.id.next);
last=(Button)findViewById(R.id.last);
improve=(Button)findViewById(R.id.improve);
lower=(Button)findViewById(R.id.lower);
img1=(ImageView)findViewById(R.id.image1);
img2=(ImageView)findViewById(R.id.image2);
img1.setImageResource(photo[current]);
View.OnClickListener click=new View.OnClickListener()
{ //明确单击头两个按钮的单击事件的事件处理器
public void onClick(View view)
{
if(view==next)//如果事件源是next按钮
img1.setImageResource(photo[(++current)%photo.length]);//显示下一张图片
if(view==last)//如果事件源是last按钮
img1.setImageResource(photo[(--current)%photo.length]);//显示上一张图片
}
};
next.setOnClickListener(click);//为next按钮注册事件监听器
last.setOnClickListener(click);//为last按钮注册事件监听器
View.OnClickListener listener=new View.OnClickListener()
{ //明确后两个按钮的事件处理器
public void onClick(View view)
{
if(view==improve) alpha=alpha+20;//如果事件源是improve按钮,透明度加20
if(view==lower) alpha=alpha-20;//如果事件源是lower按钮,透明度减去20
if(alpha>=255) alpha=255;
if(alpha<=0) alpha=0;
img1.setImageAlpha(alpha);//调用img1的设置图片透明度的方法
}
};
improve.setOnClickListener(listener);//为improve按钮注册事件监听器
lower.setOnClickListener(listener);//为lower按钮注册事件监听器
View.OnTouchListener touch=new View.OnTouchListener()
{ //明确触摸事件处理器
public boolean onTouch(View view, MotionEvent event)//注意onTouch方法的返回值和onCLick不同
{
BitmapDrawable bitmapDrawable= (BitmapDrawable)img1.getDrawable();//从img1中获得显示图片的BitmapDrawable对象
Bitmap bitmap= bitmapDrawable.getBitmap();//获取BitmapDrawable中包装的Bitmap对象
double rate=bitmap.getHeight()/img1.getHeight();
int x=(int)(event.getX()*rate);//通过event.getX()获取触摸点的X坐标
int y=(int)(event.getY()*rate);//event从哪里来呢?看看onTouch方法的参数吧
if((y+120)>bitmap.getHeight()) y=bitmap.getHeight()-120;//异常处理,防止抠图越过源位图的边界
if((x+120)>bitmap.getWidth()) x=bitmap.getWidth()-120;
Bitmap part=Bitmap.createBitmap(bitmap,x,y,120,120);//抠取图片的一部分得到新位图
img2.setImageBitmap(part);//这里用到ImageView显示图片的方法之一
return true;
}
};
img1.setOnTouchListener(touch);
}
}
注:读者可能会对以下几行代码有疑惑,为什么要用到这个rate(比率)呢?
double rate=bitmap.getHeight()/img1.getHeight();
int x=(int)(event.getX()*rate);
int y=(int)(event.getY()*rate);
解释如下:
首先,从img1得到的位图大小和img1的大小是不同的(如果我们采用fitCenter或fitStart或fitEnd缩放方式,则图片的较长边和img1相应边相等,另一条边往往不相等)。其次,event.getX()获得的是触摸点在img1上的坐标,而我们调用createBitmap函数抠图时参数x和y要求是在位图上的坐标,因此就有必要将event.getX()乘上比率rate(位图和img1不相等的边的长度之比,在这里是它们的高之比)了。
如果我们不使用rate,将会看到img2显示的图片并不是触摸点附近的图片。
app界面及功能展示如下:
透明度正常
增大透明度