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



先放上一张效果图:

Android 双指图片缩放功能 android图片手势缩放_android实现ImageView缩放

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

src内容放在真机上运行。

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

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

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

getBackground();获取即可。



1. public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,   
2.         View.OnTouchListener {


这个是我自定义ImageView的类名。

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



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;



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



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. this);  
12. new ScaleGestureDetector(context, this);  
13.     initListener();  
14. }



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

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




1. /**
2.  * 初始化事件监听
3.  */  
4. private void initListener() {  
5. // 强制设置模式  
6.     setScaleType(ScaleType.MATRIX);  
7. // 添加观察者  
8. new ViewTreeObserver.OnGlobalLayoutListener() {  
9. @Override  
10. public void onGlobalLayout() {  
11. // 移除观察者  
12. 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();这个方法,光看名字就知道要初始化图片大小,来看一看怎么

样去初始化的。



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. 1.0f / mDrawableWidth;  
13. // 图片高度度大于控件宽高,图片宽度小于控件宽度  
14. else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)  
15. 1.0f / mDrawableHeight;  
16. // 图片宽度大于控件宽度,图片高度大于控件高度  
17. else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)  
18. 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
19. // 图片宽度小于控件宽度,图片高度小于控件高度  
20. else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)  
21. 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
22.         mScale = scale;  
23. 8.0f;  
24. 0.5f;  
25.     }



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

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

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

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

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


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

Android 双指图片缩放功能 android图片手势缩放_java_02

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

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

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



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


1. /**
2.  * 移动控件中间位置
3.  */  
4. private void moveToCenter() {  
5. final float dx = mWidth / 2 - mDrawableWidth / 2;  
6. final float dy = mHeight / 2 - mDrawableHeight / 2;  
7. new Matrix();  
8. // 平移至中心  
9.     matrix.postTranslate(dx, dy);  
10. // 以控件中心作为缩放  
11. 2, mHeight / 2);  
12.     setImageMatrix(matrix);  
13. }

看注释的意思是要把它(图片)移动到屏幕的正中心,

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

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

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

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

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

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

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

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

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

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

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


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要高,所以我们只能这样子。



1. setOnTouchListener(this);


1. @Override  
2. public boolean onTouch(View v, MotionEvent event) {  
3.       
4. return scaleGestureDetector.onTouchEvent(event);  
5. }

在最后调用了scaleGestureDetector.onTouchEvent(event);这个方法。

然后手势生效了(呵呵哒

)。



1. @Override  
2. public boolean onScaleBegin(ScaleGestureDetector detector) {  
3. return true;  
4.     }

这个方法是手势执行前生效,必须return ture, 不然onScale必定失效


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



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.     /。。。


1. <span style="white-space:pre">    setImageMatrix(matrix);</span>  
2.     }

其中,scaleFactor是获得手势缩放的值(具体怎么获取的不知道),当值>1.0f时,说明两个手指的滑动距离是不断增加(相对于两个手指都down了的那一瞬间),同理<1.0f说明两个手指的滑动距离不断减少,也是相对于那一瞬间,


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大于等于最大缩放值和手指滑动为放大手势,则让手势缩放为一个恒定的最大放大值(反之同理)。


Android 双指图片缩放功能 android图片手势缩放_java_03

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



1. /**
2.  * @param matrix 矩阵
3.  * @return matrix的 l t b r 和width,height
4.  */  
5. private RectF getRectf(Matrix matrix) {  
6. new RectF();  
7. if (mDrawable == null)  
8. return null;  
9. 0, 0, mDrawableWidth, mDrawableHeight);  
10.     matrix.mapRect(f);  
11. return f;  
12. }

首先看一下这个方法,通过这个方法,可以得到矩阵matrix的N维属性,并把这N维属性赋值到一个float类型的矩形上。


在将上面的/。。。补上


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. 2 - f.right + f.width() / 2;  
33.         }  
34.   
35. if (f.height() < mHeight) {  
36. 2 - f.bottom + f.height() / 2;  
37.         }  
38.         matrix.postTranslate(dX, dY);  
39.         setImageMatrix(matrix);




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

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

以纵向为例:



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.         }


Android 双指图片缩放功能 android图片手势缩放_控件_04


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

再看第二种情况:



1. if (f.height() < mHeight) {  
2. 2 - f.bottom + f.height() / 2;  
3. }


Android 双指图片缩放功能 android图片手势缩放_Android 双指图片缩放功能_05


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

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

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


现在说一下移动图片

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. false;  
14.             downX = event.getX();  
15.             downY = event.getY();  
16. break;  
17. case MotionEvent.ACTION_POINTER_DOWN:  
18. false;  
19. break;  
20. case MotionEvent.ACTION_MOVE:  
21.             nowMovingX = event.getX();  
22.             nowMovingY = event.getY();  
23. if (!isFirstMoved) {  
24. 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. false;  
55. break;  
56.     }  
57. return scaleGestureDetector.onTouchEvent(event);  
58. }




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

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

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

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

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


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方向上,滑动前判断可不可以滑动的片段代码。


1. /**
2.  * 纠正出界的横和众线
3.  * @param dx 出界偏移的横线
4.  * @param dy 出街便宜的众线
5.  */  
6. private void remedyXAndY(float dx,float dy){  
7. if (!canSmoothX())  
8. 0);  
9. if (!canSmoothY())  
10. 0,-dy);  
11.     setImageMatrix(matrix);  
12. }

这段是用于滑动之后判断是否越界的,如果越界,把多余的dx和dy滑动回去。





完整的自定义控件代码:


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. this);  
76. new ScaleGestureDetector(context, this);  
77.         initListener();  
78.     }  
79.   
80.   
81. /**
82.      * 初始化事件监听
83.      */  
84. private void initListener() {  
85. // 强制设置模式  
86.         setScaleType(ScaleType.MATRIX);  
87. // 添加观察者  
88. new ViewTreeObserver.OnGlobalLayoutListener() {  
89. @Override  
90. public void onGlobalLayout() {  
91. // 移除观察者  
92. 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. 1.0f / mDrawableWidth;  
121. // 图片高度度大于控件宽高,图片宽度小于控件宽度  
122. else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)  
123. 1.0f / mDrawableHeight;  
124. // 图片宽度大于控件宽度,图片高度大于控件高度  
125. else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)  
126. 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
127. // 图片宽度小于控件宽度,图片高度小于控件高度  
128. else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)  
129. 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);  
130.         mScale = scale;  
131. 8.0f;  
132. 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. new Matrix();  
142. // 平移至中心  
143.         matrix.postTranslate(dx, dy);  
144. // 以控件中心作为缩放  
145. 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. new RectF();  
164. if (mDrawable == null)  
165. return null;  
166. 0, 0, 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. 2 - f.right + f.width() / 2;  
220.         }  
221.   
222. if (f.height() < mHeight) {  
223. 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. 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. false;  
259.                 downX = event.getX();  
260.                 downY = event.getY();  
261. break;  
262. case MotionEvent.ACTION_POINTER_DOWN:  
263. false;  
264. break;  
265. case MotionEvent.ACTION_MOVE:  
266.                 nowMovingX = event.getX();  
267.                 nowMovingY = event.getY();  
268. if (!isFirstMoved) {  
269. 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. 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. 0);  
335. if (!canSmoothY())  
336. 0,-dy);  
337.         setImageMatrix(matrix);  
338.     }  
339. }




activity的xml


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>