由于项目需求的原因,最近一直在研究可缩放性ImageView,用本文来记录一下最近所学:

该ImageView的实现功能有:

1)初步打开时,图片按比例满屏(填充ImageView)显示。

2)在放大缩小过程中,可以控制最大放大比例和最小缩小比例。

3)在缩放过程中,若图片的宽或高小于ImageView,则在图片在宽或高居中显示。

4)在放大后,可以移动图片,并且限制好移动的边界,不会超出图片。

5)实现双击放大或缩小的功能。(若当前图片显示为最大的比例则缩小为最小比例,若不是最小比例则放大了最大比例)

在讲代码之前,首先应该说说一个类,Matrix。因为我们在处理图片的过程中,需要图片的位移,缩放等等,而Matrix刚好就是帮我们封装好了这些数据,具体的,大家可以看看这篇文章:android 从matrix获取处理过的图片的实际宽度重点是了解Matrix里面数组的含义。

上代码,具体的说明代码都有一一介绍:

MyZoomImageView.java文件:


[java]  
1. package com.xiaoyan.doubletouch;  
2.   
3. import android.content.Context;  
4. import android.graphics.Matrix;  
5. import android.graphics.PointF;  
6. import android.graphics.drawable.Drawable;  
7. import android.util.AttributeSet;  
8. import android.util.FloatMath;  
9. import android.view.MotionEvent;  
10. import android.view.View;  
11. import android.view.ViewTreeObserver;  
12. import android.view.ViewTreeObserver.OnGlobalLayoutListener;  
13. import android.widget.ImageView;  
14.   
15. /**
16.  * 缩放ImageView
17.  * 
18.  * @author xiejinxiong
19.  * 
20.  */  
21. public class MyZoomImageView extends ImageView {  
22.   
23. /** ImageView高度 */  
24. private int imgHeight;  
25. /** ImageView宽度 */  
26. private int imgWidth;  
27. /** 图片高度 */  
28. private int intrinsicHeight;  
29. /** 图片宽度 */  
30. private int intrinsicWidth;  
31. /** 最大缩放级别 */  
32. private float mMaxScale = 2.0f;  
33. /** 最小缩放级别 */  
34. private float mMinScale = 0.2f;  
35. /** 用于记录拖拉图片移动的坐标位置 */  
36. private Matrix matrix = new Matrix();  
37. /** 用于记录图片要进行拖拉时候的坐标位置 */  
38. private Matrix currentMatrix = new Matrix();  
39. /** 记录第一次点击的时间 */  
40. private long firstTouchTime = 0;  
41. /** 时间点击的间隔 */  
42. private int intervalTime = 250;  
43. /** 第一次点完坐标 */  
44. private PointF firstPointF;  
45.   
46. public MyZoomImageView(Context context) {  
47. super(context);  
48.         initUI();  
49.     }  
50.   
51. public MyZoomImageView(Context context, AttributeSet attrs) {  
52. super(context, attrs);  
53.         initUI();  
54.     }  
55.   
56. public MyZoomImageView(Context context, AttributeSet attrs, int defStyle) {  
57. super(context, attrs, defStyle);  
58. // TODO Auto-generated constructor stub  
59.         initUI();  
60.     }  
61.   
62. /**
63.      * 初始化UI
64.      */  
65. private void initUI() {  
66.   
67. this.setScaleType(ScaleType.FIT_CENTER);  
68. this.setOnTouchListener(new TouchListener());  
69.   
70.         getImageViewWidthHeight();  
71.         getIntrinsicWidthHeight();  
72.     }  
73.   
74. /**
75.      * 获得图片内在宽高
76.      */  
77. private void getIntrinsicWidthHeight() {  
78. this.getDrawable();  
79.   
80. // 初始化bitmap的宽高  
81.         intrinsicHeight = drawable.getIntrinsicHeight();  
82.         intrinsicWidth = drawable.getIntrinsicWidth();  
83.     }  
84.   
85. private final class TouchListener implements OnTouchListener {  
86.   
87. /** 记录是拖拉照片模式还是放大缩小照片模式 */  
88. private int mode = 0;// 初始状态  
89. /** 拖拉照片模式 */  
90. private static final int MODE_DRAG = 1;  
91. /** 放大缩小照片模式 */  
92. private static final int MODE_ZOOM = 2;  
93. /** 用于记录开始时候的坐标位置 */  
94. private PointF startPoint = new PointF();  
95. /** 两个手指的开始距离 */  
96. private float startDis;  
97. /** 两个手指的中间点 */  
98. private PointF midPoint;  
99.   
100. public boolean onTouch(View v, MotionEvent event) {  
101.   
102. /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */  
103. switch (event.getAction() & MotionEvent.ACTION_MASK) {// 单点监听和多点触碰监听  
104. // 手指压下屏幕  
105. case MotionEvent.ACTION_DOWN:  
106.                 mode = MODE_DRAG;  
107. // 记录ImageView当前的移动位置  
108.                 currentMatrix.set(getImageMatrix());  
109.                 startPoint.set(event.getX(), event.getY());  
110.                 matrix.set(currentMatrix);  
111.                 makeImageViewFit();  
112. break;  
113. // 手指在屏幕上移动,改事件会被不断触发  
114. case MotionEvent.ACTION_MOVE:  
115. // 拖拉图片  
116. if (mode == MODE_DRAG) {  
117. // System.out.println("ACTION_MOVE_____MODE_DRAG");  
118. float dx = event.getX() - startPoint.x; // 得到x轴的移动距离  
119. float dy = event.getY() - startPoint.y; // 得到x轴的移动距离  
120. // 在没有移动之前的位置上进行移动z  
121.                     matrix.set(currentMatrix);  
122. float[] values = new float[9];  
123.                     matrix.getValues(values);  
124.                     dx = checkDxBound(values, dx);  
125.                     dy = checkDyBound(values, dy);  
126.                     matrix.postTranslate(dx, dy);  
127.   
128.                 }  
129. // 放大缩小图片  
130. else if (mode == MODE_ZOOM) {  
131. float endDis = distance(event);// 结束距离  
132. if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10  
133. float scale = endDis / startDis;// 得到缩放倍数  
134.                         matrix.set(currentMatrix);  
135.   
136. float[] values = new float[9];  
137.                         matrix.getValues(values);  
138.   
139.                         scale = checkFitScale(scale, values);  
140.   
141.                         matrix.postScale(scale, scale, midPoint.x, midPoint.y);  
142.   
143.                     }  
144.                 }  
145. break;  
146. // 手指离开屏幕  
147. case MotionEvent.ACTION_UP:  
148.                 setDoubleTouchEvent(event);  
149.   
150. case MotionEvent.ACTION_POINTER_UP:  
151. // System.out.println("ACTION_POINTER_UP");  
152. 0;  
153. // matrix.set(currentMatrix);  
154. float[] values = new float[9];  
155.                 matrix.getValues(values);  
156.                 makeImgCenter(values);  
157. break;  
158. // 当屏幕上已经有触点(手指),再有一个触点压下屏幕  
159. case MotionEvent.ACTION_POINTER_DOWN:  
160. // System.out.println("ACTION_POINTER_DOWN");  
161.                 mode = MODE_ZOOM;  
162. /** 计算两个手指间的距离 */  
163.                 startDis = distance(event);  
164. /** 计算两个手指间的中间点 */  
165. if (startDis > 10f) { // 两个手指并拢在一起的时候像素大于10  
166.                     midPoint = mid(event);  
167. // 记录当前ImageView的缩放倍数  
168.                     currentMatrix.set(getImageMatrix());  
169.                 }  
170. break;  
171.             }  
172.             setImageMatrix(matrix);  
173. return true;  
174.         }  
175.   
176. /** 计算两个手指间的距离 */  
177. private float distance(MotionEvent event) {  
178. float dx = event.getX(1) - event.getX(0);  
179. float dy = event.getY(1) - event.getY(0);  
180. /** 使用勾股定理返回两点之间的距离 */  
181. return FloatMath.sqrt(dx * dx + dy * dy);  
182.         }  
183.   
184. /** 计算两个手指间的中间点 */  
185. private PointF mid(MotionEvent event) {  
186. float midX = (event.getX(1) + event.getX(0)) / 2;  
187. float midY = (event.getY(1) + event.getY(0)) / 2;  
188. return new PointF(midX, midY);  
189.         }  
190.   
191. /**
192.          * 和当前矩阵对比,检验dy,使图像移动后不会超出ImageView边界
193.          * 
194.          * @param values
195.          * @param dy
196.          * @return
197.          */  
198. private float checkDyBound(float[] values, float dy) {  
199.   
200. float height = imgHeight;  
201. if (intrinsicHeight * values[Matrix.MSCALE_Y] < height)  
202. return 0;  
203. if (values[Matrix.MTRANS_Y] + dy > 0)  
204.                 dy = -values[Matrix.MTRANS_Y];  
205. else if (values[Matrix.MTRANS_Y] + dy < -(intrinsicHeight  
206.                     * values[Matrix.MSCALE_Y] - height))  
207.                 dy = -(intrinsicHeight * values[Matrix.MSCALE_Y] - height)  
208.                         - values[Matrix.MTRANS_Y];  
209. return dy;  
210.         }  
211.   
212. /**
213.          * 和当前矩阵对比,检验dx,使图像移动后不会超出ImageView边界
214.          * 
215.          * @param values
216.          * @param dx
217.          * @return
218.          */  
219. private float checkDxBound(float[] values, float dx) {  
220.   
221. float width = imgWidth;  
222. if (intrinsicWidth * values[Matrix.MSCALE_X] < width)  
223. return 0;  
224. if (values[Matrix.MTRANS_X] + dx > 0)  
225.                 dx = -values[Matrix.MTRANS_X];  
226. else if (values[Matrix.MTRANS_X] + dx < -(intrinsicWidth  
227.                     * values[Matrix.MSCALE_X] - width))  
228.                 dx = -(intrinsicWidth * values[Matrix.MSCALE_X] - width)  
229.                         - values[Matrix.MTRANS_X];  
230. return dx;  
231.         }  
232.   
233. /**
234.          * MSCALE用于处理缩放变换
235.          * 
236.          * 
237.          * MSKEW用于处理错切变换
238.          * 
239.          * 
240.          * MTRANS用于处理平移变换
241.          */  
242.   
243. /**
244.          * 检验scale,使图像缩放后不会超出最大倍数
245.          * 
246.          * @param scale
247.          * @param values
248.          * @return
249.          */  
250. private float checkFitScale(float scale, float[] values) {  
251. if (scale * values[Matrix.MSCALE_X] > mMaxScale)  
252.                 scale = mMaxScale / values[Matrix.MSCALE_X];  
253. if (scale * values[Matrix.MSCALE_X] < mMinScale)  
254.                 scale = mMinScale / values[Matrix.MSCALE_X];  
255. return scale;  
256.         }  
257.   
258. /**
259.          * 促使图片居中
260.          * 
261.          * @param values
262.          *            (包含着图片变化信息)
263.          */  
264. private void makeImgCenter(float[] values) {  
265.   
266. // 缩放后图片的宽高  
267. float zoomY = intrinsicHeight * values[Matrix.MSCALE_Y];  
268. float zoomX = intrinsicWidth * values[Matrix.MSCALE_X];  
269. // 图片左上角Y坐标  
270. float leftY = values[Matrix.MTRANS_Y];  
271. // 图片左上角X坐标  
272. float leftX = values[Matrix.MTRANS_X];  
273. // 图片右下角Y坐标  
274. float rightY = leftY + zoomY;  
275. // 图片右下角X坐标  
276. float rightX = leftX + zoomX;  
277.   
278. // 使图片垂直居中  
279. if (zoomY < imgHeight) {  
280. float marY = (imgHeight - zoomY) / 2.0f;  
281. 0, marY - leftY);  
282.             }  
283.   
284. // 使图片水平居中  
285. if (zoomX < imgWidth) {  
286.   
287. float marX = (imgWidth - zoomX) / 2.0f;  
288. 0);  
289.   
290.             }  
291.   
292. // 使图片缩放后上下不留白(即当缩放后图片的大小大于imageView的大小,但是上面或下面留出一点空白的话,将图片移动占满空白处)  
293. if (zoomY >= imgHeight) {  
294. if (leftY > 0) {// 判断图片上面留白  
295. 0, -leftY);  
296.                 }  
297. if (rightY < imgHeight) {// 判断图片下面留白  
298. 0, imgHeight - rightY);  
299.                 }  
300.             }  
301.   
302. // 使图片缩放后左右不留白  
303. if (zoomX >= imgWidth) {  
304. if (leftX > 0) {// 判断图片左边留白  
305. 0);  
306.                 }  
307. if (rightX < imgWidth) {// 判断图片右边不留白  
308. 0);  
309.                 }  
310.             }  
311.         }  
312.   
313.     }  
314.   
315. /**
316.      * 获取ImageView的宽高
317.      */  
318. private void getImageViewWidthHeight() {  
319.         ViewTreeObserver vto2 = getViewTreeObserver();  
320. new OnGlobalLayoutListener() {  
321. @SuppressWarnings("deprecation")  
322. public void onGlobalLayout() {  
323. this);  
324.                 imgWidth = getWidth();  
325.                 imgHeight = getHeight();  
326.   
327.             }  
328.         });  
329.     }  
330.   
331. /**
332.      * 使得ImageView一开始便显示最适合的宽高比例,便是刚好容下的样子
333.      */  
334. private void makeImageViewFit() {  
335. if (getScaleType() != ScaleType.MATRIX) {  
336.             setScaleType(ScaleType.MATRIX);  
337.   
338. 1.0f, 1.0f, imgWidth / 2, imgHeight / 2);  
339.         }  
340.     }  
341.   
342. /**
343.      * 双击事件触发
344.      * 
345.      * @param values
346.      */  
347. private void setDoubleTouchEvent(MotionEvent event) {  
348.   
349. float values[] = new float[9];  
350.         matrix.getValues(values);  
351. // 存储当前时间  
352. long currentTime = System.currentTimeMillis();  
353. // 判断两次点击间距时间是否符合  
354. if (currentTime - firstTouchTime >= intervalTime) {  
355.             firstTouchTime = currentTime;  
356. new PointF(event.getX(), event.getY());  
357. else {  
358. // 判断两次点击之间的距离是否小于30f  
359. if (Math.abs(event.getX() - firstPointF.x) < 30f  
360.                     && Math.abs(event.getY() - firstPointF.y) < 30f) {  
361. // 判断当前缩放比例与最大最小的比例  
362. if (values[Matrix.MSCALE_X] < mMaxScale) {  
363.                     matrix.postScale(mMaxScale / values[Matrix.MSCALE_X],  
364.                             mMaxScale / values[Matrix.MSCALE_X], event.getX(),  
365.                             event.getY());  
366. else {  
367.                     matrix.postScale(mMinScale / values[Matrix.MSCALE_X],  
368.                             mMinScale / values[Matrix.MSCALE_X], event.getX(),  
369.                             event.getY());  
370.                 }  
371.             }  
372.   
373.         }  
374.     }  
375.   
376. /**
377.      * 设置图片的最大和最小的缩放比例
378.      * 
379.      * @param mMaxScale
380.      * @param mMinScale
381.      */  
382. public void setPicZoomHeightWidth(float mMaxScale, float mMinScale) {  
383. this.mMaxScale = mMaxScale;  
384. this.mMinScale = mMinScale;  
385.     }  
386.   
387. }


xml文件:


[html] 
1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
2. xmlns:tools="http://schemas.android.com/tools"  
3. android:layout_width="match_parent"  
4. android:layout_height="match_parent"  
5. tools:context="com.xiaoyan.doubletouch.MainActivity" >  
6.   
7. <com.xiaoyan.doubletouch.MyZoomImageView  
8. android:id="@+id/imageView"  
9. android:layout_width="fill_parent"  
10. android:layout_height="fill_parent"  
11. android:src="@drawable/a" />  
12.   
13. </RelativeLayout>



具体实现效果:

最初显示:


android imageView 同比放大 imageview放大缩小_缩放

放大显示:


android imageView 同比放大 imageview放大缩小_android_02