(三)多点触控之自由移动缩放后的图片

在上一篇文章中,将图片的自由缩放功能基本上完成了。效果还不错。如果你还没读过,可以点击下面的链接:
http://www.cnblogs.com/fuly550871915/p/4939954.html

接下来这个项目要往前走,在自由缩放的基础上实现自由移动。要用的知识点就是OnTouchListener对移动手势的监控。在写代码之前我们应该考虑下面的几个问题:

(1)什么时候可以移动?当图片比屏幕大时才需要移动,如果图片在屏幕内显示,没必要移动。(2)当移动的距离达到多少时才触发移动效果?在这里android提供了一个系统的临界值,直接使用即可。(3)怎么使图片移动?得到移动距离,利用Matrix.postTeanslate方法即可。

这些问题都想清楚了,实现的逻辑就不难了。

下面修改ZoomImageView,其中的代码为:

  1 package com.example.view;
  2
  3 import android.annotation.SuppressLint;
  4 import android.content.Context;
  5 import android.graphics.Matrix;
  6 import android.graphics.RectF;
  7 import android.graphics.drawable.Drawable;
  8 import android.util.AttributeSet;
  9 import android.util.Log;
 10 import android.view.MotionEvent;
 11 import android.view.ScaleGestureDetector;
 12 import android.view.ScaleGestureDetector.OnScaleGestureListener;
 13 import android.view.View;
 14 import android.view.ViewConfiguration;
 15 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 16 import android.view.View.OnTouchListener;
 17 import android.widget.ImageView;
 18
 19 public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,
 20 OnScaleGestureListener, OnTouchListener
 21 {
 22     private boolean mOnce = false;//是否执行了一次
 23
 24     /**
 25      * 初始缩放的比例
 26      */
 27     private float initScale;
 28     /**
 29      * 缩放比例
 30      */
 31     private float midScale;
 32     /**
 33      * 可放大的最大比例
 34      */
 35     private float maxScale;
 36     /**
 37      * 缩放矩阵
 38      */
 39     private Matrix scaleMatrix;
 40
 41     /**
 42      * 缩放的手势监控类
 43      */
 44     private ScaleGestureDetector mScaleGestureDetector;
 45
 46     //==========================下面是自由移动的成员变量======================================
 47     /**
 48      * 上一次移动的手指个数,也可以说是多点个数
 49      */
 50     private int mLastPoint;
 51     /**
 52      * 上次的中心点的x位置
 53      */
 54     private float mLastX;
 55     /**
 56      * 上一次中心点的y位置
 57      */
 58     private float mLastY;
 59     /**
 60      * 一个临界值,即是否触发移动的临界值
 61      */
 62     private float mScaleSlop;
 63     /**
 64      * 是否可移动
 65      */
 66     private boolean isCanDrag = false;
 67
 68
 69
 70     public ZoomImageView(Context context)
 71     {
 72         this(context,null);
 73     }
 74     public ZoomImageView(Context context, AttributeSet attrs)
 75     {
 76         this(context, attrs,0);
 77
 78     }
 79     public ZoomImageView(Context context, AttributeSet attrs, int defStyle)
 80     {
 81         super(context, attrs, defStyle);
 82
 83         scaleMatrix = new Matrix();
 84
 85         setScaleType(ScaleType.MATRIX);
 86
 87         mScaleGestureDetector = new ScaleGestureDetector(context, this);
 88         //触摸回调
 89         setOnTouchListener(this);
 90         //获得系统给定的触发移动效果的临界值
 91         mScaleSlop = ViewConfiguration.get(context).getScaledTouchSlop();
 92     }
 93
 94     /**
 95      * 该方法在view与window绑定时被调用,且只会被调用一次,其在view的onDraw方法之前调用
 96      */
 97     protected void onAttachedToWindow()
 98     {
 99         super.onAttachedToWindow();
100         //注册监听器
101         getViewTreeObserver().addOnGlobalLayoutListener(this);
102     }
103
104     /**
105      * 该方法在view被销毁时被调用
106      */
107     @SuppressLint("NewApi") protected void onDetachedFromWindow()
108     {
109         super.onDetachedFromWindow();
110         //取消监听器
111         getViewTreeObserver().removeOnGlobalLayoutListener(this);
112     }
113
114     /**
115      * 当一个view的布局加载完成或者布局发生改变时,OnGlobalLayoutListener会监听到,调用该方法
116      * 因此该方法可能会被多次调用,需要在合适的地方注册和取消监听器
117      */
118     public void onGlobalLayout()
119     {
120         if(!mOnce)
121         {
122             //获得当前view的Drawable
123             Drawable d = getDrawable();
124
125             if(d == null)
126             {
127                 return;
128             }
129
130             //获得Drawable的宽和高
131             int dw = d.getIntrinsicWidth();
132             int dh = d.getIntrinsicHeight();
133
134             //获取当前view的宽和高
135             int width = getWidth();
136             int height = getHeight();
137
138             //缩放的比例,scale可能是缩小的比例也可能是放大的比例,看它的值是大于1还是小于1
139             float scale = 1.0f;
140
141             //如果仅仅是图片宽度比view宽度大,则应该将图片按宽度缩小
142             if(dw>width&&dh<height)
143             {
144                 scale = width*1.0f/dw;
145             }
146             //如果图片和高度都比view的大,则应该按最小的比例缩小图片
147             if(dw>width&&dh>height)
148             {
149                 scale = Math.min(width*1.0f/dw, height*1.0f/dh);
150             }
151             //如果图片宽度和高度都比view的要小,则应该按最小的比例放大图片
152             if(dw<width&&dh<height)
153             {
154                 scale = Math.min(width*1.0f/dw, height*1.0f/dh);
155             }
156             //如果仅仅是高度比view的大,则按照高度缩小图片即可
157             if(dw<width&&dh>height)
158             {
159                 scale = height*1.0f/dh;
160             }
161
162             //初始化缩放的比例
163             initScale = scale;
164             midScale = initScale*2;
165             maxScale = initScale*4;
166
167             //移动图片到达view的中心
168             int dx = width/2 - dw/2;
169             int dy = height/2 - dh/2;
170             scaleMatrix.postTranslate(dx, dy);
171
172             //缩放图片
173             scaleMatrix.postScale(initScale, initScale, width/2, height/2);
174
175             setImageMatrix(scaleMatrix);
176             mOnce = true;
177         }
178
179     }
180     /**
181      * 获取当前已经缩放的比例
182      * @return  因为x方向和y方向比例相同,所以只返回x方向的缩放比例即可
183      */
184     private float getDrawableScale()
185     {
186
187         float[] values = new float[9];
188         scaleMatrix.getValues(values);
189
190         return values[Matrix.MSCALE_X];
191
192     }
193
194     /**
195      * 缩放手势进行时调用该方法
196      *
197      * 缩放范围:initScale~maxScale
198      */
199     public boolean onScale(ScaleGestureDetector detector)
200     {
201
202         if(getDrawable() == null)
203         {
204             return true;//如果没有图片,下面的代码没有必要运行
205         }
206
207         float scale = getDrawableScale();
208         //获取当前缩放因子
209         float scaleFactor = detector.getScaleFactor();
210
211         if((scale<maxScale&&scaleFactor>1.0f)||(scale>initScale&&scaleFactor<1.0f))
212         {
213             //如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
214             if(scale*scaleFactor<initScale&&scaleFactor<1.0f)
215             {
216                 scaleFactor = initScale/scale;
217             }
218             //如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
219             if(scale*scaleFactor>maxScale&&scaleFactor>1.0f)
220             {
221                 scaleFactor = maxScale/scale;
222             }
223
224 //            scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
225             scaleMatrix.postScale(scaleFactor, scaleFactor,detector.getFocusX(),
226                     detector.getFocusY());
227
228             checkBoderAndCenter();//处理缩放后图片边界与屏幕有间隙或者不居中的问题
229
230
231             setImageMatrix(scaleMatrix);//千万不要忘记设置这个,我总是忘记
232         }
233
234
235
236         return true;
237     }
238     /**
239      * 处理缩放后图片边界与屏幕有间隙或者不居中的问题
240      */
241     private void checkBoderAndCenter()
242     {
243        RectF rectf = getDrawableRectF();
244
245        int width = getWidth();
246        int height = getHeight();
247
248        float delaX =0;
249        float delaY = 0;
250
251        if(rectf.width()>=width)
252        {
253            if(rectf.left>0)
254            {
255              delaX = - rectf.left;
256            }
257
258            if(rectf.right<width)
259            {
260                delaX = width - rectf.right;
261            }
262        }
263
264        if(rectf.height()>=height)
265        {
266            if(rectf.top>0)
267            {
268                delaY = -rectf.top;
269            }
270            if(rectf.bottom<height)
271            {
272                delaY = height - rectf.bottom;
273            }
274        }
275
276        if(rectf.width()<width)
277        {
278            delaX = width/2 - rectf.right+ rectf.width()/2;
279        }
280
281        if(rectf.height()<height)
282        {
283            delaY =  height/2 - rectf.bottom+ rectf.height()/2;
284        }
285
286        scaleMatrix.postTranslate(delaX, delaY);
287     }
288     /**
289      * 获取图片根据矩阵变换后的四个角的坐标,即left,top,right,bottom
290      * @return
291      */
292     private RectF getDrawableRectF()
293     {
294         Matrix matrix = scaleMatrix;
295         RectF rectf = new RectF();
296         Drawable d = getDrawable();
297         if(d != null)
298         {
299
300             rectf.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
301         }
302
303         matrix.mapRect(rectf);
304         return  rectf;
305     }
306     /**
307      * 缩放手势开始时调用该方法
308      */
309     public boolean onScaleBegin(ScaleGestureDetector detector)
310     {
311         //返回为true,则缩放手势事件往下进行,否则到此为止,即不会执行onScale和onScaleEnd方法
312         return true;
313     }
314     /**
315      * 缩放手势完成后调用该方法
316      */
317     public void onScaleEnd(ScaleGestureDetector detector)
318     {
319
320
321     }
322
323     /**
324      * 监听触摸事件
325      */
326     public boolean onTouch(View v, MotionEvent event)
327     {
328
329         if(mScaleGestureDetector != null)
330         {
331             //将触摸事件传递给手势缩放这个类
332             mScaleGestureDetector.onTouchEvent(event);
333         }
334
335
336         //获得多点个数,也叫屏幕上手指的个数
337         int pointCount = event.getPointerCount();
338
339         float x =0;
340         float y =0;//中心点的x和y
341
342         for(int i=0;i<pointCount;i++)
343         {
344             x+=event.getX(i);
345             y+=event.getY(i);
346         }
347
348         //求出中心点的位置
349         x/= pointCount;
350         y/= pointCount;
351
352         //如果手指的数量发生了改变,则不移动
353         if(mLastPoint != pointCount)
354         {
355             isCanDrag = false;
356             mLastX = x;
357             mLastY = y;
358
359         }
360         mLastPoint = pointCount;
361
362
363         switch(event.getAction())
364         {
365         case MotionEvent.ACTION_MOVE:
366
367             //求出移动的距离
368             float dx = x - mLastX;
369             float dy = y- mLastY;
370
371             if(!isCanDrag)
372             {
373                 isCanDrag = isCanDrag(dx,dy);
374             }
375
376             if(isCanDrag)
377             {
378                 //如果图片能正常显示,就不需要移动了
379                 RectF rectf = getDrawableRectF();
380                 if(rectf.width()<=getWidth())
381                 {
382                     dx = 0;
383                 }
384                 if(rectf.height()<=getHeight())
385                 {
386                     dy = 0;
387                 }
388
389
390                 //开始移动
391                 scaleMatrix.postTranslate(dx, dy);
392                 //处理移动后图片边界与屏幕有间隙或者不居中的问题
393                 checkBoderAndCenterWhenMove();
394                 setImageMatrix(scaleMatrix);
395             }
396
397             mLastX = x;
398             mLastY = y;
399
400
401             break;
402         case MotionEvent.ACTION_UP:
403         case MotionEvent.ACTION_CANCEL:
404                     mLastPoint = 0;
405             break;
406
407         }
408
409         return true;
410     }
411     /**
412      * 处理移动后图片边界与屏幕有间隙或者不居中的问题
413      * 这跟我们前面写的代码很像
414      */
415     private void checkBoderAndCenterWhenMove() {
416
417         RectF rectf = getDrawableRectF();
418
419         float delaX = 0;
420         float delaY = 0;
421         int width = getWidth();
422         int height = getHeight();
423
424         if(rectf.width()>width&&rectf.left>0)
425         {
426             delaX = - rectf.left;
427         }
428         if(rectf.width()>width&&rectf.right<width)
429         {
430             delaX = width - rectf.right;
431         }
432         if(rectf.height()>height&&rectf.top>0)
433         {
434             delaY = - rectf.top;
435         }
436         if(rectf.height()>height&&rectf.bottom<height)
437         {
438             delaY = height - rectf.bottom;
439         }
440
441         scaleMatrix.postTranslate(delaX, delaY);
442     }
443     /**
444      * 判断是否触发移动效果
445      * @param dx
446      * @param dy
447      * @return
448      */
449     private boolean isCanDrag(float dx, float dy) {
450
451         return Math.sqrt(dx*dx+dy*dy)>mScaleSlop;
452     }
453
454
455
456
457 }

红色部分就是增加的代码了。在这里简单解释一下。首先拿到系统给定的判定是否触发移动效果的临界值,即91行获得mScaleSlop,然后在isCanDrag方法中根据移动的距离,判定是否足够触发移动效果。在onTouch方法中,编写具体的移动逻辑。由于是多点触控,即屏幕上可能不止一个手指。因此通过计算得到中心触控的位置x和y,即第349行和350行所做的事情。另外通过mLastX和mLastY保存之前移动的中心位置。通过计算当前中心位置与之前保存的中心位置的差值,就可以得到需要图片移动的距离了,即第368和第369行代码所做的事情。之后就可以判断是否触发移动了,如果触发了那就移动呗。移动完成后,再做些善后处理。大体逻辑就是这样子。很简单,代码也很详实。不再解释了。哦,对了,因为移动过程中,也可能会出现与屏幕间有空隙,因此需要checkBoderAndCenterWhenMove一下。这里面的代码跟之前写的的差不多了,就不多余解释了,原理一样。

好了,运行程序吧,效果如下:

效果达到了。是不是发现代码越写越少,越简单呢?因为这个项目快要完成了,基本没有什么大的逻辑了。下面就 快马加鞭,实现双击放大与缩小图片的功能吧:《(四)双击放大与缩小图片》

时间: 2024-10-10 07:24:16

(三)多点触控之自由移动缩放后的图片的相关文章

Android实现多点触控,自由缩放图片

Android多点触控涉及到的知识点 1.ScaleGestureDetector 2.OnScaleGestureListener 3.Matrix 4.OnTouchListener 四个知识点需要了解一下,需要注意的是Matrix在内存中是一个一维数组,操控图片的Matrxi是一个3X3的矩阵,在内存中也就是一个大小为9的一维数组. 实现多点触控,自由变化图片 1. ImageView的基础上继承 2.因为要在图片加载完成就获取到相关的属性,所以实现OnGlobalLayoutListen

Android多点触控技术实战,自由地对图片进行缩放和移动

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100327 在上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果,虽然这种效果很炫很酷,但其实还只能算是一个半成品,因为照片墙中所有的图片都是只能看不能点的.因此本篇文章中,我们就来对这一功能进行完善,加入点击图片就能浏览大图的功能,并且在浏览大图的时候还可以通过多点触控的方式对图片进行缩放. 如果你还没有看过 Android瀑布流照片墙实现,体验不规则排列的美感

图片缩放和多点触控

实现图片的缩放并不难,主要需要一些计算和对图片的平移及缩放操作 主布局: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_

Android多点触控(图片的缩放Demo)

本文主要介绍Android的多点触控,使用了一个图片缩放的实例,来更好的说明其原理.需要实现OnTouchListener接口,重写其中的onTouch方法. 实现效果图: 源代码: 布局文件: activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools&quo

多点触控 地图缩放

使用地图App中,我们经常需要对界面进行缩放操作来更加便利的查看位置.那么在Appium中怎样去模拟这类操作呢? MultiAction MultiAction 是多点触控的类,可以模拟用户多点操作.主要包含 add() 和 perform() 两个方法, MultiAction可以结合前面所学的 ActionTouch可以模拟出用户的多个手指滑动的操作效果: from appium.webdriver.common.multi_action import MultiAction from ap

unity3d 触屏多点触控(旋转与缩放)

unity3d 触屏多点触控(旋转与缩放) /*Touch OrbitProgrammed by: Randal J. Phillips (Caliber Mengsk)Original Creation Date: 12/16/2011Last Updated:                   12/16/2011Desctiption: Simple orbit by one touch and drag, as well as pinch to zoom with two finger

(五)多点触控之兼容ViewPager

在上一篇文章中,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大与缩小的功能.已经可以投入使用这个控件了.下面我们就在ViewPager中使用这个控件.如果你还没读过上一篇文章,可以点击下面的链接: http://www.cnblogs.com/fuly550871915/p/4940193.html 一.在ViewPager中使用自定义的ZoomImageView 快速的代建起ViewPager吧.修改activity_main.xml中的代码,如下: <Relati

关于android多点触控

最近项目需要一个多点触控缩放的功能.然后上网查了下资料 总结一下: 首先android sdk版本很重要,比如你在AndroidManifest.xml中指定android:minSdkVersion="4"  并且建工程的时候选的是android 2.2就表示你的应用兼容android1.6~android2.2 之间的版本.但是多点触控的API在1.6~~2,1~~,2,2~~三个版本中都是不一样的.比如android2.2中onTouchEvent(MotionEvent eve

Android笔记:触摸事件的分析与总结----多点触控

一.多点触控 当多点同时触摸屏幕时,系统将会产生如下的触摸事件: 1.ACTION_DOWN:触摸屏幕的第一个点.此时手势开始.该点的数据通常在MotionEvent事件队列索引位置0处. 2.ACTION_POINTER_DOWN:除了第一个点的其他触摸点数据.该点的数据的索引位置由getActionIndex()方法返回. 3.ACTION_MOVE:在手势过程中发生的一次变化. 4.ACTION_POINTER_UP:当不是第一个点的其他点UP后触发. 5.ACTION_UP:当手势中的最