Android实现 通过手势随意缩放、移动ImageView图片

听说写博客可以丰富简历?好吧,正好无聊,我也写写看~



先放上一张效果图:


在这里,我对自己的笔记本全屏截图,然后当作自定义ImageView的

src内容放在真机上运行。

可以看到这里的图片是可以移动和缩放的。

在这里先说清一点,如果在xml的控件上设置src,则需要在代码上

通过getDrawable();获取,如果是通过setBackGround的,则通过

getBackground();获取即可。

[java]  view plain  copy
  1. public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,   
  2.         View.OnTouchListener {  
这个是我自定义ImageView的类名。

我在这里实现了一些待会会用到的接口。



[java]  view plain  copy
  1. /** 
  2.  * 控件宽度 
  3.  */  
  4. private int mWidth;  
  5. /** 
  6.  * 控件高度 
  7.  */  
  8. private int mHeight;  
  9. /** 
  10.  * 拿到src的图片 
  11.  */  
  12. private Drawable mDrawable;  
  13. /** 
  14.  * 图片宽度(使用前判断mDrawable是否null) 
  15.  */  
  16. private int mDrawableWidth;  
  17. /** 
  18.  * 图片高度(使用前判断mDrawable是否null) 
  19.  */  
  20. private int mDrawableHeight;  
  21.   
  22. /** 
  23.  * 初始化缩放值 
  24.  */  
  25. private float mScale;  
  26.   
  27. /** 
  28.  * 双击图片的缩放值 
  29.  */  
  30. private float mDoubleClickScale;  
  31.   
  32. /** 
  33.  * 最大的缩放值 
  34.  */  
  35. private float mMaxScale;  
  36.   
  37. /** 
  38.  * 最小的缩放值 
  39.  */  
  40. private float mMinScale;  
  41.   
  42. private ScaleGestureDetector scaleGestureDetector;  
  43. /** 
  44.  * 当前有着缩放值、平移值的矩阵。 
  45.  */  
  46. private Matrix matrix;  

这些是我定义出来的一些成员变量,每个变量我都写上了作用。


[java]  view plain  copy
  1. public MyImageView(Context context) {  
  2.     this(context, null);  
  3. }  
  4.   
  5. public MyImageView(Context context, AttributeSet attrs) {  
  6.     this(context, attrs, 0);  
  7. }  
  8.   
  9. public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  10.     super(context, attrs, defStyleAttr);  
  11.     setOnTouchListener(this);  
  12.     scaleGestureDetector = new ScaleGestureDetector(context, this);  
  13.     initListener();  
  14. }  

这里是三个标准的构造器,直接用短的引用长的就是了。

先看一看initListener();干了什么事情。


[java]  view plain  copy
  1. /** 
  2.  * 初始化事件监听 
  3.  */  
  4. private void initListener() {  
  5.     // 强制设置模式  
  6.     setScaleType(ScaleType.MATRIX);  
  7.     // 添加观察者  
  8.     getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
  9.         @Override  
  10.         public void onGlobalLayout() {  
  11.             // 移除观察者  
  12.             getViewTreeObserver().removeOnGlobalLayoutListener(this);  
  13.             // 获取控件大小  
  14.             mWidth = getWidth();  
  15.             mHeight = getHeight();  
  16.   
  17.             //通过getDrawable获得Src的图片  
  18.             mDrawable = getDrawable();  
  19.             if (mDrawable == null)  
  20.                 return;  
  21.             mDrawableWidth = mDrawable.getIntrinsicWidth();  
  22.             mDrawableHeight = mDrawable.getIntrinsicHeight();  
  23.             initImageViewSize();  
  24.             moveToCenter();  
  25.         }  
  26.     });  
  27. }  

这里唯一要注意的是我在初始化监听这个方法内,强制了ImageView的scaleType

模式为:MATRIX,因为等会会用到矩阵来缩放和平移,因此强制一下它的scaleT-

ype。


initImageViewSize();这个方法,光看名字就知道要初始化图片大小,来看一看怎么

样去初始化的。


[java]  view plain  copy
  1. /** 
  2.      * 初始化资源图片宽高 
  3.      */  
  4.     private void initImageViewSize() {  
  5.         if (mDrawable == null)  
  6.             return;  
  7.   
  8.         // 缩放值  
  9.         float scale = 1.0f;  
  10.         // 图片宽度大于控件宽度,图片高度小于控件高度  
  11.         if (mDrawableWidth > mWidth && mDrawableHeight < mHeight)  
  12.             scale = mWidth * 1.0f / mDrawableWidth;  
  13.             // 图片高度度大于控件宽高,图片宽度小于控件宽度  
  14.         else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)  
  15.             scale = mHeight * 1.0f / mDrawableHeight;  
  16.             // 图片宽度大于控件宽度,图片高度大于控件高度  
  17.         else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)  
  18.             scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
  19.             // 图片宽度小于控件宽度,图片高度小于控件高度  
  20.         else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)  
  21.             scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
  22.         mScale = scale;  
  23.         mMaxScale = mScale * 8.0f;  
  24.         mMinScale = mScale * 0.5f;  
  25.     }  

先判断一下有没有src资源,没有的话,这个方法调用也没意义了。

这里是要图片的宽或高其中一边充满着屏幕,在最后的三句话,意思就是,

首先,我们假设初始化缩放后的图片面积是A,允许它的最大放大倍数为8A,

最小缩小倍数为0.5A,就是这个意思,(是基于初始化缩放后的面积,而不是

原图的面积,想想这是笔记本的屏幕面积,很大吧!


moveToCenter();这个方法之前,先看一下不调用这个方法的效果图会咋样


scaleType为MATRIX属性的图片都是不经过缩放直接显示在屏幕左上角,

这时候肯定会有童鞋问,咦,刚刚不是缩放过么,我这时候只能说,孩子,

你太天真了,那只是理论上的缩放值,还没经过实操呢(滑稽)大笑


好吧,接下来就看看moveToCenter();做了什么事情,

[java]  view plain  copy
  1. /** 
  2.  * 移动控件中间位置 
  3.  */  
  4. private void moveToCenter() {  
  5.     final float dx = mWidth / 2 - mDrawableWidth / 2;  
  6.     final float dy = mHeight / 2 - mDrawableHeight / 2;  
  7.     matrix = new Matrix();  
  8.     // 平移至中心  
  9.     matrix.postTranslate(dx, dy);  
  10.     // 以控件中心作为缩放  
  11.     matrix.postScale(mScale, mScale, mWidth / 2, mHeight / 2);  
  12.     setImageMatrix(matrix);  
  13. }  
看注释的意思是要把它(图片)移动到屏幕的正中心,

dx的意思是取横方向上,控件中心到图片中心的值,如果大于0就向右移动,

反之向左移动相应的绝对值。

dy则换成纵向方向就是了。

在这里实例化了matrix对象(初始化一次就行),至于为什么只需要初始化一次,

因为图片的缩放值和平移值,都是通过matrix保存的,如果再一次初始化,缩放值

和平移值等等数据都会被清空。

我是先让它平时到控件正中心,然后以控件中心缩放mScale,mScale在initImageViewSize();的时候已经赋值了。

至于先缩放后平移,应该也是可以得,但可能计算公式相对麻烦些,

在这里本着方便为主的原则,就不再作计算了。

接下来会说到这个东西scaleGestureDetector = new ScaleGestureDetector(context, this);

通过这个方法,实现了监听事件,是手势滑动的监听事件。

[java]  view plain  copy
  1. @Override  
  2. public boolean onScale(ScaleGestureDetector detector) {  
  3.       
  4.     return true;  
  5. }  
  6.   
  7.   
  8. @Override  
  9. public boolean onScaleBegin(ScaleGestureDetector detector) {  
  10.     return true;  
  11. }  
  12.   
  13. @Override  
  14. public void onScaleEnd(ScaleGestureDetector detector) {  
  15.       
  16. }  
但是,虽然实现了监听,但是然并卵,因为onTouch事件中没有它(scaleGestureDetector),在这里 ,重写onTouchEvent是没用的,因为onTouchEventListener的优先级比 onTouchEvent要高,所以我们只能这样子。

[java]  view plain  copy
  1. setOnTouchListener(this);  
[java]  view plain  copy
  1. @Override  
  2. public boolean onTouch(View v, MotionEvent event) {  
  3.       
  4.     return scaleGestureDetector.onTouchEvent(event);  
  5. }  
在最后调用了scaleGestureDetector.onTouchEvent(event);这个方法。

然后手势生效了(呵呵哒得意)。

[java]  view plain  copy
  1. @Override  
  2.     public boolean onScaleBegin(ScaleGestureDetector detector) {  
  3.         return true;  
  4.     }  
这个方法是手势执行前生效,必须return ture, 不然onScale必定失效

现在重点说一下onScale,因为这个方法是处理手势的缩放,

[java]  view plain  copy
  1. @Override  
  2. public boolean onScale(ScaleGestureDetector detector) {  
  3.     if (mDrawable == null) {  
  4.         return true;  
  5.     }  
  6.     // 系统定义的缩放值  
  7.     float scaleFactor = detector.getScaleFactor();  
  8.     // 获取已经缩放的值  
  9.     float scale = getmScale();  
  10.     float scaleResult = scale * scaleFactor;  
  11.     if (scaleResult >= mMaxScale && scaleFactor > 1.0f)  
  12.         scaleFactor = mMaxScale / scale;  
  13.     if (scaleResult <= mMinScale && scaleFactor < 1.0f)   
  14.         scaleFactor = mMinScale / scale;  
  15.       
  16.   
  17.     matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());  
  18.   
  19.     /。。。  
[java]  view plain  copy
  1. <span style="white-space:pre">    setImageMatrix(matrix);</span>  
  2.     }  
其中,scaleFactor是获得手势缩放的值(具体怎么获取的不知道),当值>1.0f时,说明两个手指的滑动距离是不断增加(相对于两个手指都down了的那一瞬间),同理<1.0f说明两个手指的滑动距离不断减少,也是相对于那一瞬间,

[java]  view plain  copy
  1. /** 
  2.  * @return 当前缩放的值 
  3.  */  
  4. private float getmScale() {  
  5.     float[] floats = new float[9];  
  6.     matrix.getValues(floats);  
  7.     return floats[Matrix.MSCALE_X];  
  8. }  
通过这个方法 ,拿到了之前matrix对象的scaleX值( X和Y都没所谓,因为在这里都是一个值 ),然后将当前的scale*手势滑动的缩放值,得到最新的缩放值scaleResult,在这里做了一个最大放大值和最小缩小值得处理,如果 scaleResult大于等于最大缩放值和手指滑动为放大手势,则让手势缩放为一个恒定的最大放大值(反之同理)。


看效果图后,会觉得比较奇葩,因为缩小的时候,位置好像偏了!(原本是在控件正中心)。



[java]  view plain  copy
  1. /** 
  2.  * @param matrix 矩阵 
  3.  * @return matrix的 l t b r 和width,height 
  4.  */  
  5. private RectF getRectf(Matrix matrix) {  
  6.     RectF f = new RectF();  
  7.     if (mDrawable == null)  
  8.         return null;  
  9.     f.set(00, mDrawableWidth, mDrawableHeight);  
  10.     matrix.mapRect(f);  
  11.     return f;  
  12. }  
首先看一下这个方法,通过这个方法,可以得到矩阵matrix的N维属性,并把这N维属性赋值到一个float类型的矩形上。


在将上面的/。。。补上

[java]  view plain  copy
  1. <span style="white-space:pre">    </span>RectF f = getRectf(matrix);  
  2.         float dX = 0.0f;  
  3.         float dY = 0.0f;  
  4.         // 图片高度大于控件高度  
  5.         if (f.height() >= mHeight) {  
  6.             // 图片顶部出现空白  
  7.             if (f.top > 0) {  
  8.                 // 往上移动  
  9.                 dY = -f.top;  
  10.             }  
  11.             // 图片底部出现空白  
  12.             if (f.bottom < mHeight) {  
  13.                 // 往下移动  
  14.                 dY = mHeight - f.bottom;  
  15.             }  
  16.         }  
  17.         // 图片宽度大于控件宽度  
  18.         if (f.width() >= mWidth) {  
  19.             // 图片左边出现空白  
  20.             if (f.left > 0) {  
  21.                 // 往左边移动  
  22.                 dX = -f.left;  
  23.             }  
  24.             // 图片右边出现空白  
  25.             if (f.right < mWidth) {  
  26.                 // 往右边移动  
  27.                 dX = mWidth - f.right;  
  28.             }  
  29.         }  
  30.   
  31.         if (f.width() < mWidth) {  
  32.             dX = mWidth / 2 - f.right + f.width() / 2;  
  33.         }  
  34.   
  35.         if (f.height() < mHeight) {  
  36.             dY = mHeight / 2 - f.bottom + f.height() / 2;  
  37.         }  
  38.         matrix.postTranslate(dX, dY);  
  39.         setImageMatrix(matrix);  


首先获取矩阵matrix的N维并赋值在f身上。

在这里其实是细分为2种情况(横竖类似的合一)

以纵向为例:

[java]  view plain  copy
  1. // 图片高度大于控件高度  
  2.         if (f.height() >= mHeight) {  
  3.             // 图片顶部出现空白  
  4.             if (f.top > 0) {  
  5.                 // 往上移动  
  6.                 dY = -f.top;  
  7.             }  
  8.             // 图片底部出现空白  
  9.             if (f.bottom < mHeight) {  
  10.                 // 往下移动  
  11.                 dY = mHeight - f.bottom;  
  12.             }  
  13.         }  

大概就是这个意思:当图片高度大于等于控件高度的时候,坚决不让控件高度方向上出现白色位置,此时,假设当图片和控件高度完全相同的时候,是不是图片的纵向刚好和控件完全重叠呢?

再看第二种情况:

[java]  view plain  copy
  1. if (f.height() < mHeight) {  
  2.     dY = mHeight / 2 - f.bottom + f.height() / 2;  
  3. }  

之前说到,当图片和控件高度完全相同的时候,是不是图片的纵向刚好和控件完全重叠呢?其实,这句话不应该用假设句,而是肯定句,所以,想想,如果图片纵向恰好小于控件高度那么一点点,是不是图片纵向上瞬间就被移动到控件的中间呢?

这种情况的横向方向的道理完全一致,在此也说明一个道理,可以把复杂的事情细分了处理,反正方法是顺着执行的。

这样,图片的缩放就处理完了。


现在说一下移动图片

[java]  view plain  copy
  1. private float downX;  
  2. private float downY;  
  3. private float nowMovingX;  
  4. private float nowMovingY;  
  5. private float lastMovedX;  
  6. private float lastMovedY;  
  7. private boolean isFirstMoved = false;  
  8.   
  9. @Override  
  10. public boolean onTouch(View v, MotionEvent event) {  
  11.     switch (event.getAction() & MotionEvent.ACTION_MASK) {  
  12.         case MotionEvent.ACTION_DOWN:  
  13.             isFirstMoved = false;  
  14.             downX = event.getX();  
  15.             downY = event.getY();  
  16.             break;  
  17.         case MotionEvent.ACTION_POINTER_DOWN:  
  18.             isFirstMoved = false;  
  19.             break;  
  20.         case MotionEvent.ACTION_MOVE:  
  21.             nowMovingX = event.getX();  
  22.             nowMovingY = event.getY();  
  23.             if (!isFirstMoved) {  
  24.                 isFirstMoved = true;  
  25.                 lastMovedX = nowMovingX;  
  26.                 lastMovedY = nowMovingY;  
  27.             }  
  28.             float dX = 0.0f;  
  29.             float dY = 0.0f;  
  30.             RectF rectf = getRectf(matrix);  
  31.             // 判断滑动方向  
  32.             final float scrollX = nowMovingX - lastMovedX;  
  33.             // 判断滑动方向  
  34.             final float scrollY = nowMovingY - lastMovedY;  
  35.             // 图片高度大于控件高度  
  36.             if (rectf.height() > mHeight && canSmoothY()) {  
  37.                 dY = nowMovingY - lastMovedY;  
  38.             }  
  39.   
  40.             // 图片宽度大于控件宽度  
  41.             if (rectf.width() > mWidth && canSmoothX()) {  
  42.                 dX = nowMovingX - lastMovedX;  
  43.             }  
  44.             matrix.postTranslate(dX, dY);  
  45.   
  46.             remedyXAndY(dX,dY);  
  47.   
  48.             lastMovedX = nowMovingX;  
  49.             lastMovedY = nowMovingY;  
  50.             break;  
  51.         case MotionEvent.ACTION_UP:  
  52.             break;  
  53.         case MotionEvent.ACTION_POINTER_UP:  
  54.             isFirstMoved = false;  
  55.             break;  
  56.     }  
  57.     return scaleGestureDetector.onTouchEvent(event);  
  58. }  


MotionEvent.ACTION_POINTER_DOWN;这个也是压下的时候,区别在于只有不是第一根手指压下的时候才执行,

所以,我在压下的动作都初始化isFirstMoved=false;

当移动的时候,ACTION_MOVE也会执行。

由于移动的时候处理逻辑少的问题,出现屏幕越界后明显的白边反弹,因此在这里编辑了一部分代码。。。

滑动前,先判断能否滑动,滑动后,再次判断是否越界,因此,有效解决了白边反弹现象。

[java]  view plain  copy
  1. /** 
  2.  * 判断x方向上能不能滑动 
  3.  * @return 可以滑动返回true 
  4.  */  
  5. private boolean canSmoothX(){  
  6.     RectF rectf = getRectf(matrix);  
  7.     if (rectf.left >0 || rectf.right <getWidth())  
  8.         return false;  
  9.     return true;  
  10. }  
  11.   
  12. /** 
  13.  * 判断y方向上可不可以滑动 
  14.  * @return 可以滑动返回true 
  15.  */  
  16. private boolean canSmoothY(){  
  17.     RectF rectf = getRectf(matrix);  
  18.     if (rectf.top>0 || rectf.bottom < getHeight())  
  19.         return false;  
  20.     return true;  
  21. }  
以上是x和y方向上,滑动前判断可不可以滑动的片段代码。

[java]  view plain  copy
  1. /** 
  2.  * 纠正出界的横和众线 
  3.  * @param dx 出界偏移的横线 
  4.  * @param dy 出街便宜的众线 
  5.  */  
  6. private void remedyXAndY(float dx,float dy){  
  7.     if (!canSmoothX())  
  8.         matrix.postTranslate(-dx,0);  
  9.     if (!canSmoothY())  
  10.         matrix.postTranslate(0,-dy);  
  11.     setImageMatrix(matrix);  
  12. }  
这段是用于滑动之后判断是否越界的,如果越界,把多余的dx和dy滑动回去。



完整的自定义控件代码:

[java]  view plain  copy
  1. package com.test.gesturedemo.view;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Matrix;  
  5. import android.graphics.RectF;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.util.AttributeSet;  
  8. import android.view.MotionEvent;  
  9. import android.view.ScaleGestureDetector;  
  10. import android.view.View;  
  11. import android.view.ViewTreeObserver;  
  12. import android.widget.ImageView;  
  13.   
  14. /** 
  15.  * Created by 13798 on 2016/6/3. 
  16.  */  
  17. public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {  
  18.     /** 
  19.      * 控件宽度 
  20.      */  
  21.     private int mWidth;  
  22.     /** 
  23.      * 控件高度 
  24.      */  
  25.     private int mHeight;  
  26.     /** 
  27.      * 拿到src的图片 
  28.      */  
  29.     private Drawable mDrawable;  
  30.     /** 
  31.      * 图片宽度(使用前判断mDrawable是否null) 
  32.      */  
  33.     private int mDrawableWidth;  
  34.     /** 
  35.      * 图片高度(使用前判断mDrawable是否null) 
  36.      */  
  37.     private int mDrawableHeight;  
  38.   
  39.     /** 
  40.      * 初始化缩放值 
  41.      */  
  42.     private float mScale;  
  43.   
  44.     /** 
  45.      * 双击图片的缩放值 
  46.      */  
  47.     private float mDoubleClickScale;  
  48.   
  49.     /** 
  50.      * 最大的缩放值 
  51.      */  
  52.     private float mMaxScale;  
  53.   
  54.     /** 
  55.      * 最小的缩放值 
  56.      */  
  57.     private float mMinScale;  
  58.   
  59.     private ScaleGestureDetector scaleGestureDetector;  
  60.     /** 
  61.      * 当前有着缩放值、平移值的矩阵。 
  62.      */  
  63.     private Matrix matrix;  
  64.   
  65.     public MyImageView(Context context) {  
  66.         this(context, null);  
  67.     }  
  68.   
  69.     public MyImageView(Context context, AttributeSet attrs) {  
  70.         this(context, attrs, 0);  
  71.     }  
  72.   
  73.     public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  74.         super(context, attrs, defStyleAttr);  
  75.         setOnTouchListener(this);  
  76.         scaleGestureDetector = new ScaleGestureDetector(context, this);  
  77.         initListener();  
  78.     }  
  79.   
  80.   
  81.     /** 
  82.      * 初始化事件监听 
  83.      */  
  84.     private void initListener() {  
  85.         // 强制设置模式  
  86.         setScaleType(ScaleType.MATRIX);  
  87.         // 添加观察者  
  88.         getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
  89.             @Override  
  90.             public void onGlobalLayout() {  
  91.                 // 移除观察者  
  92.                 getViewTreeObserver().removeGlobalOnLayoutListener(this);  
  93.                 // 获取控件大小  
  94.                 mWidth = getWidth();  
  95.                 mHeight = getHeight();  
  96.   
  97.                 //通过getDrawable获得Src的图片  
  98.                 mDrawable = getDrawable();  
  99.                 if (mDrawable == null)  
  100.                     return;  
  101.                 mDrawableWidth = mDrawable.getIntrinsicWidth();  
  102.                 mDrawableHeight = mDrawable.getIntrinsicHeight();  
  103.                 initImageViewSize();  
  104.                 moveToCenter();  
  105.             }  
  106.         });  
  107.     }  
  108.   
  109.     /** 
  110.      * 初始化资源图片宽高 
  111.      */  
  112.     private void initImageViewSize() {  
  113.         if (mDrawable == null)  
  114.             return;  
  115.   
  116.         // 缩放值  
  117.         float scale = 1.0f;  
  118.         // 图片宽度大于控件宽度,图片高度小于控件高度  
  119.         if (mDrawableWidth > mWidth && mDrawableHeight < mHeight)  
  120.             scale = mWidth * 1.0f / mDrawableWidth;  
  121.             // 图片高度度大于控件宽高,图片宽度小于控件宽度  
  122.         else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)  
  123.             scale = mHeight * 1.0f / mDrawableHeight;  
  124.             // 图片宽度大于控件宽度,图片高度大于控件高度  
  125.         else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)  
  126.             scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
  127.             // 图片宽度小于控件宽度,图片高度小于控件高度  
  128.         else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)  
  129.             scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
  130.         mScale = scale;  
  131.         mMaxScale = mScale * 8.0f;  
  132.         mMinScale = mScale * 0.5f;  
  133.     }  
  134.   
  135.     /** 
  136.      * 移动控件中间位置 
  137.      */  
  138.     private void moveToCenter() {  
  139.         final float dx = mWidth / 2 - mDrawableWidth / 2;  
  140.         final float dy = mHeight / 2 - mDrawableHeight / 2;  
  141.         matrix = new Matrix();  
  142.         // 平移至中心  
  143.         matrix.postTranslate(dx, dy);  
  144.         // 以控件中心作为缩放  
  145.         matrix.postScale(mScale, mScale, mWidth / 2, mHeight / 2);  
  146.         setImageMatrix(matrix);  
  147.     }  
  148.   
  149.     /** 
  150.      * @return 当前缩放的值 
  151.      */  
  152.     private float getmScale() {  
  153.         float[] floats = new float[9];  
  154.         matrix.getValues(floats);  
  155.         return floats[Matrix.MSCALE_X];  
  156.     }  
  157.   
  158.     /** 
  159.      * @param matrix 矩阵 
  160.      * @return matrix的 l t b r 和width,height 
  161.      */  
  162.     private RectF getRectf(Matrix matrix) {  
  163.         RectF f = new RectF();  
  164.         if (mDrawable == null)  
  165.             return null;  
  166.         f.set(00, mDrawableWidth, mDrawableHeight);  
  167.         matrix.mapRect(f);  
  168.         return f;  
  169.     }  
  170.   
  171.     @Override  
  172.     public boolean onScale(ScaleGestureDetector detector) {  
  173.         if (mDrawable == null) {  
  174.             return true;  
  175.         }  
  176.         // 系统定义的缩放值  
  177.         float scaleFactor = detector.getScaleFactor();  
  178.         // 获取已经缩放的值  
  179.         float scale = getmScale();  
  180.         float scaleResult = scale * scaleFactor;  
  181.         if (scaleResult >= mMaxScale && scaleFactor > 1.0f)  
  182.             scaleFactor = mMaxScale / scale;  
  183.         if (scaleResult <= mMinScale && scaleFactor < 1.0f)  
  184.             scaleFactor = mMinScale / scale;  
  185.   
  186.         matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());  
  187.   
  188.         RectF f = getRectf(matrix);  
  189.         float dX = 0.0f;  
  190.         float dY = 0.0f;  
  191.         // 图片高度大于控件高度  
  192.         if (f.height() >= mHeight) {  
  193.             // 图片顶部出现空白  
  194.             if (f.top > 0) {  
  195.                 // 往上移动  
  196.                 dY = -f.top;  
  197.             }  
  198.             // 图片底部出现空白  
  199.             if (f.bottom < mHeight) {  
  200.                 // 往下移动  
  201.                 dY = mHeight - f.bottom;  
  202.             }  
  203.         }  
  204.         // 图片宽度大于控件宽度  
  205.         if (f.width() >= mWidth) {  
  206.             // 图片左边出现空白  
  207.             if (f.left > 0) {  
  208.                 // 往左边移动  
  209.                 dX = -f.left;  
  210.             }  
  211.             // 图片右边出现空白  
  212.             if (f.right < mWidth) {  
  213.                 // 往右边移动  
  214.                 dX = mWidth - f.right;  
  215.             }  
  216.         }  
  217.   
  218.         if (f.width() < mWidth) {  
  219.             dX = mWidth / 2 - f.right + f.width() / 2;  
  220.         }  
  221.   
  222.         if (f.height() < mHeight) {  
  223.             dY = mHeight / 2 - f.bottom + f.height() / 2;  
  224.         }  
  225.         matrix.postTranslate(dX, dY);  
  226.         setImageMatrix(matrix);  
  227.         return true;  
  228.     }  
  229.   
  230.   
  231.     @Override  
  232.     public boolean onScaleBegin(ScaleGestureDetector detector) {  
  233.         return true;  
  234.     }  
  235.   
  236.     @Override  
  237.     public void onScaleEnd(ScaleGestureDetector detector) {  
  238.         float scale = getmScale();  
  239.         if (scale < mScale) {  
  240.             matrix.postScale(mScale / scale, mScale / scale, mWidth / 2, mHeight / 2);  
  241.             setImageMatrix(matrix);  
  242.         }  
  243.     }  
  244.   
  245.   
  246.     private float downX;  
  247.     private float downY;  
  248.     private float nowMovingX;  
  249.     private float nowMovingY;  
  250.     private float lastMovedX;  
  251.     private float lastMovedY;  
  252.     private boolean isFirstMoved = false;  
  253.   
  254.     @Override  
  255.     public boolean onTouch(View v, MotionEvent event) {  
  256.         switch (event.getAction() & MotionEvent.ACTION_MASK) {  
  257.             case MotionEvent.ACTION_DOWN:  
  258.                 isFirstMoved = false;  
  259.                 downX = event.getX();  
  260.                 downY = event.getY();  
  261.                 break;  
  262.             case MotionEvent.ACTION_POINTER_DOWN:  
  263.                 isFirstMoved = false;  
  264.                 break;  
  265.             case MotionEvent.ACTION_MOVE:  
  266.                 nowMovingX = event.getX();  
  267.                 nowMovingY = event.getY();  
  268.                 if (!isFirstMoved) {  
  269.                     isFirstMoved = true;  
  270.                     lastMovedX = nowMovingX;  
  271.                     lastMovedY = nowMovingY;  
  272.                 }  
  273.                 float dX = 0.0f;  
  274.                 float dY = 0.0f;  
  275.                 RectF rectf = getRectf(matrix);  
  276.                 // 判断滑动方向  
  277.                 final float scrollX = nowMovingX - lastMovedX;  
  278.                 // 判断滑动方向  
  279.                 final float scrollY = nowMovingY - lastMovedY;  
  280.                 // 图片高度大于控件高度  
  281.                 if (rectf.height() > mHeight && canSmoothY()) {  
  282.                     dY = nowMovingY - lastMovedY;  
  283.                 }  
  284.   
  285.                 // 图片宽度大于控件宽度  
  286.                 if (rectf.width() > mWidth && canSmoothX()) {  
  287.                     dX = nowMovingX - lastMovedX;  
  288.                 }  
  289.                 matrix.postTranslate(dX, dY);  
  290.   
  291.                 remedyXAndY(dX,dY);  
  292.   
  293.                 lastMovedX = nowMovingX;  
  294.                 lastMovedY = nowMovingY;  
  295.                 break;  
  296.             case MotionEvent.ACTION_UP:  
  297.                 break;  
  298.             case MotionEvent.ACTION_POINTER_UP:  
  299.                 isFirstMoved = false;  
  300.                 break;  
  301.         }  
  302.         return scaleGestureDetector.onTouchEvent(event);  
  303.     }  
  304.   
  305.     /** 
  306.      * 判断x方向上能不能滑动 
  307.      * @return 可以滑动返回true 
  308.      */  
  309.     private boolean canSmoothX(){  
  310.         RectF rectf = getRectf(matrix);  
  311.         if (rectf.left >0 || rectf.right <getWidth())  
  312.             return false;  
  313.         return true;  
  314.     }  
  315.   
  316.     /** 
  317.      * 判断y方向上可不可以滑动 
  318.      * @return 可以滑动返回true 
  319.      */  
  320.     private boolean canSmoothY(){  
  321.         RectF rectf = getRectf(matrix);  
  322.         if (rectf.top>0 || rectf.bottom < getHeight())  
  323.             return false;  
  324.         return true;  
  325.     }  
  326.   
  327.     /** 
  328.      * 纠正出界的横和众线 
  329.      * @param dx 出界偏移的横线 
  330.      * @param dy 出街便宜的众线 
  331.      */  
  332.     private void remedyXAndY(float dx,float dy){  
  333.         if (!canSmoothX())  
  334.             matrix.postTranslate(-dx,0);  
  335.         if (!canSmoothY())  
  336.             matrix.postTranslate(0,-dy);  
  337.         setImageMatrix(matrix);  
  338.     }  
  339. }  


activity的xml

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent">  
  7.   
  8.     <com.test.gesturedemo.view.MyImageView  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent"  
  11.         android:scaleType="matrix"  
  12.         android:src="@mipmap/tt1"/>  
  13.   
  14. </RelativeLayout>  
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值