本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020

上节实现了查看图片及录音的功能,其中查看图片,可以调用系统的图库来查看图片,也可以自定义Activity来查看图片,今天就在上节的基础上,实现手势缩放与拖拽图片。

想必大家都用过系统的图库,浏览图片时,可以通过手势放大或缩小图片,旋转图片,拖拽图片等功能,我们也为自已定义的查看图片的Activity增加手势缩放与拖拽图片的功能,效果如下图:

上面四幅图中,演示了通过手势(多点触控)来缩小,放大,拖拽图片。

这里主要是用到了多点触控,所以我们首先要知道多点和单点的区别。

单手指操作过程: ACTION_DOWN-ACTION_MOVE-ACTIOIN_UP

多手指操作过程:ACTION_DOWN-ACTION_POINTER_DOWN-ACTION_MOVE-ACTION_POINTER_UP-ACTION_UP

一般实现图片的缩放都是用Matrix的postScale方法,那么通过手势(多点)来缩放图片当然也不例外,区别就是通过手指的滑动来判断缩放的比例及中心位置,具体做法如下:

手势缩放图片的步骤:

1. 设置ImageView的scaleType属性为matrix。

因为实现图片的缩放要用到Matrix,所以这个属性是前提,可以在xml里设置ImageView设置,android:scaleType="matrix",或者在代码里设置imageView.setScaleType(ScaleType.matrix)

2. 给ImageView绑定触摸监听器

 //触摸事件
img.setOnTouchListener(new TouchEvent());

3. 在触摸事件中实现多手指缩放及拖拽图片

这是本节的核心,主要是要先判断MotionEvent的类型,是单手指还是多手指,可以通过event.getActionMasked()来获得,并设立三个标志,分别用于判断当前操作是拖拽,缩放还是无操作,如果是拖拽,则需要记录手指的起始位置及终点位置,然后利用Matrix的postTranslate方法来实现图片的移动。如果是缩放,则需要先计算图片缩放的比例及位置,在计算缩放比例时,又需要先知道多手指移动的直径,通过多手指移动前后的比例来得到缩放的比例;而要计算图片缩放的位置,只需要计算出手指移动前后的中点即可。

4. 控制缩放比例

其实,完成前3步就已经能实现通过手势来控制图片的缩放和移动,但是你会发现,这时,图片可以放大的无限大,也可以缩小到无限小,而且,不管图片是在放大状态,还是缩小状态,都会随你的手指的移动而移动到任何地方。这显然是不符合实际使用的,所以这就需要控制图片的缩放的比例,主要代码如下:

    //控制缩放比例
private void controlScale(){
float values[] = new float[9];
matrix.getValues(values);
if(mode == ZOOM){
if(values[0] < MINSCALER)
matrix.setScale(MINSCALER, MINSCALER);
else if(values[0] > MAXSCALER)
matrix.setScale(MAXSCALER, MAXSCALER);
}
}

5. 设置图片居中显示

通过第4步,可以控制图片的缩放比例,这样,图片就会有一个最大的和最小的绽放比例,当计算出的缩放比例小于最小的缩放比例时,就会设置当前的缩放比例为最小的缩放比例,当大于最大的缩放比例时,就设置当前图片的缩放比例为最大的缩放比例。

但是,还有一个问题,就是,这时不管图片的放大还是缩小状态,都会随着你的手指移动,但在实际过程中,我们往往需要图片的缩放后都是在控件的中心位置,即,设置图片居中显示,代码如下:

  //自动居中  左右及上下都居中  
protected void center()
{
center(true,true);
}

private void center(boolean horizontal, boolean vertical)
{
Matrix m = new Matrix();
m.set(matrix);
RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
m.mapRect(rect);
float height = rect.height();
float width = rect.width();
float deltaX = 0, deltaY = 0;
if (vertical)
{
int screenHeight = dm.heightPixels; //手机屏幕分辨率的高度
//int screenHeight = 400;
if (height < screenHeight)
{
deltaY = (screenHeight - height)/2 - rect.top;
}else if (rect.top > 0)
{
deltaY = -rect.top;
}else if (rect.bottom < screenHeight)
{
deltaY = screenHeight - rect.bottom;
}
}

if (horizontal)
{
int screenWidth = dm.widthPixels; //手机屏幕分辨率的宽度
//int screenWidth = 400;
if (width < screenWidth)
{
deltaX = (screenWidth - width)/2 - rect.left;
}else if (rect.left > 0)
{
deltaX = -rect.left;
}else if (rect.right < screenWidth)
{
deltaX = screenWidth - rect.right;
}
}
matrix.postTranslate(deltaX, deltaY);
}
通过居中设置后,这样图片在未占满屏幕时,是不能进行将其移动到其他位置的,只有在图片大于屏幕时,也可以移动图片从而查看图片的不同位置。

基本上,通过这5步,就已经可以实现图片手势(多点)缩放的功能,而且也可以控制图片的缩放比例及居中显示。下面给出整个代码:

public class ShowPicture extends Activity {
private ImageView img;
private Bitmap bm;
private DisplayMetrics dm;
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
private PointF mid = new PointF();
private PointF start = new PointF();
private static int DRAG = 2;
private static int ZOOM = 1;
private static int NONE = 0;
private int mode = 0;
private float oldDist = 1f;
private static float MINSCALER = 0.3f;
private static float MAXSCALER = 3.0f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.activity_show_picture);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_add);
//设置标题
TextView tv_title = (TextView)findViewById(R.id.tv_title);
tv_title.setText("查看图片");
Button bt_back = (Button)findViewById(R.id.bt_back);
bt_back.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
ShowPicture.this.finish();
}
});
Button bt_del = (Button)findViewById(R.id.bt_save);
bt_del.setBackgroundResource(R.drawable.paint_icon_delete);

dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm); //获取分辨率



img = (ImageView)findViewById(R.id.iv_showPic);

Intent intent = this.getIntent();
String imgPath = intent.getStringExtra("imgPath");
bm = BitmapFactory.decodeFile(imgPath);
//设置居中显示
savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
img.setImageMatrix(savedMatrix);
//绑定图片
img.setImageBitmap(bm);
//触摸事件
img.setOnTouchListener(new TouchEvent());
}

//添加触摸事件,实现图片的手势缩放
class TouchEvent implements OnTouchListener{
@Override
public boolean onTouch(View view, MotionEvent event) {
switch(event.getActionMasked()){
//单击触控,用于拖动
case MotionEvent.ACTION_DOWN :
matrix.set(img.getImageMatrix());
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
//多点触控,按下时
case MotionEvent.ACTION_POINTER_DOWN :
oldDist = getSpacing(event);
savedMatrix.set(matrix);
getMidPoint(mid,event);
mode = ZOOM;
break;
//多点触控,抬起时
case MotionEvent.ACTION_POINTER_UP :
mode = NONE;
break;
case MotionEvent.ACTION_MOVE :
if(mode == DRAG){
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
}
//缩放
else if(mode == ZOOM){
//取得多指移动的直径,如果大于10,则认为是缩放手势
float newDist = getSpacing(event);
if(newDist > 10){
matrix.set(savedMatrix);
float scale = newDist / oldDist;

matrix.postScale(scale, scale,mid.x,mid.y);
}
}
break;
}
img.setImageMatrix(matrix);
controlScale();
//setCenter();
center();
return true;
}
}

//求距离
private float getSpacing(MotionEvent event){
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}

//求中点
private void getMidPoint(PointF mid,MotionEvent event){
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
mid.set(x / 2, y / 2);
}
//控制缩放比例
private void controlScale(){
float values[] = new float[9];
matrix.getValues(values);
if(mode == ZOOM){
if(values[0] < MINSCALER)
matrix.setScale(MINSCALER, MINSCALER);
else if(values[0] > MAXSCALER)
matrix.setScale(MAXSCALER, MAXSCALER);
}
}
//自动居中 左右及上下都居中
protected void center()
{
center(true,true);
}

private void center(boolean horizontal, boolean vertical)
{
Matrix m = new Matrix();
m.set(matrix);
RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
m.mapRect(rect);
float height = rect.height();
float width = rect.width();
float deltaX = 0, deltaY = 0;
if (vertical)
{
int screenHeight = dm.heightPixels; //手机屏幕分辨率的高度
//int screenHeight = 400;
if (height < screenHeight)
{
deltaY = (screenHeight - height)/2 - rect.top;
}else if (rect.top > 0)
{
deltaY = -rect.top;
}else if (rect.bottom < screenHeight)
{
deltaY = screenHeight - rect.bottom;
}
}

if (horizontal)
{
int screenWidth = dm.widthPixels; //手机屏幕分辨率的宽度
//int screenWidth = 400;
if (width < screenWidth)
{
deltaX = (screenWidth - width)/2 - rect.left;
}else if (rect.left > 0)
{
deltaX = -rect.left;
}else if (rect.right < screenWidth)
{
deltaX = screenWidth - rect.right;
}
}
matrix.postTranslate(deltaX, deltaY);
}
}

注:

另外还有一个问题就是,图片的初始居中问题,在ImageView中可以设置属性android:scaleType="fitCenter"来实现图片的居中显示,但是这里是要实现手势缩放图片,所以需要将该属性设置为android:scaleType="matrix",但同时,这也带来了一个问题,就是在初始时,图片不能居中。

在网上找过解决办法,第一种方法,就是在XML文件里先设置ImageVIew的scaleType属性为fitCenter,然后在ImageVIew的setOnTouchListener()方法之前设置setScaleType(ScaleType.MATRIX); 但是这种方法我没有成功,图片初始还是在左上角;另一种方法,就是先计算手机屏幕的高和宽,求出使图片居中的点(x,y),然后通过Matrix的平移到该位置,最后通过setImageMatrix()为ImageView绑定该Matrix,以实现图片初始居中显示。而我最后采用的也就是第二种方法,代码如下:

private DisplayMetrics dm;  

... ...

dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm); //获取分辨率

... ...

//设置居中显示
savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
img.setImageMatrix(savedMatrix);

如果大家有更好的办法,能使图片初始时居中,请分享下。


更多相关文章

  1. Android BitmapShader 实战 实现圆形、圆角图片
  2. Android多线程下载远程图片【转】
  3. 如何设置webview的初始缩放/宽度
  4. Button点击缩放动画效果
  5. Android开发学习之ImageView手势拖拽、缩放、旋转
  6. Android界面的.9.png图片显示出错,怎么回事啊?
  7. 关于Android4.0之上的ListView显示从网络上获取图片和文字
  8. Android 图片添加水印图片或者文字
  9. 拍照后获取不了图片!

随机推荐

  1. Android 之 自定义控件用法介绍
  2. android一些小技巧
  3. 周记:Class4
  4. Android中属性gravity和layout_grativy的
  5. Android ExpandableListView的使用
  6. SQLite 锁机制与事务简介
  7. 【Android】Android控件之Seekbar拖动条
  8. android linearlayout 把控件view置底部(
  9. Android 自定义圆角按钮
  10. android中设置分隔线几种方法