(五)多点触控之兼容ViewPager

在上一篇文章中,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大与缩小的功能。已经可以投入使用这个控件了。下面我们就在ViewPager中使用这个控件。如果你还没读过上一篇文章,可以点击下面的链接:

http://www.cnblogs.com/fuly550871915/p/4940193.html

一、在ViewPager中使用自定义的ZoomImageView

快速的代建起ViewPager吧。修改activity_main.xml中的代码,如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

     <android.support.v4.view.ViewPager
       android:id="@+id/id_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         >

     </android.support.v4.view.ViewPager>

</RelativeLayout>

然后修改MainActivity中的代码,如下:

 1 package com.example.zoom;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5
 6 import com.example.view.ZoomImageView;
 7
 8 import android.os.Bundle;
 9 import android.support.v4.view.PagerAdapter;
10 import android.support.v4.view.ViewPager;
11 import android.view.View;
12 import android.view.ViewGroup;
13 import android.widget.ImageView;
14 import android.app.Activity;
15
16 public class MainActivity extends Activity {
17
18
19     private ViewPager mViewPager;
20     private int[] imgIds = new int[]{R.drawable.mingxing0403,R.drawable.qw,
21             R.drawable.ic_launcher};
22
23     private List<ImageView> mImageViews =new ArrayList<ImageView>();
24
25
26     protected void onCreate(Bundle savedInstanceState) {
27         super.onCreate(savedInstanceState);
28         setContentView(R.layout.activity_main);
29
30         mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
31
32         for(int i=0;i<imgIds.length;i++)
33         {
34             ZoomImageView ziv = new ZoomImageView(getApplicationContext());
35             ziv.setImageResource(imgIds[i]);
36             mImageViews.add(ziv);
37         }
38
39
40         mViewPager.setAdapter(new PagerAdapter() {
41
42
43
44             public boolean isViewFromObject(View arg0, Object arg1) {
45
46                 return arg0 == arg1;
47             }
48
49
50             public int getCount() {
51
52                 return mImageViews.size();
53             }
54
55
56
57
58             public void destroyItem(ViewGroup container, int position,
59                     Object object) {
60
61                 container.removeView(mImageViews.get(position));
62             }
63
64
65             public Object instantiateItem(ViewGroup container, int position) {
66                 container.addView(mImageViews.get(position));
67                 return mImageViews.get(position);
68             }
69
70
71         });
72     }
73
74 }

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

红色代码是我们添加的。在这里,只需要请求父类ViewPager不要拦截触摸事件即可。然后我们运行程序,效果如下图:

依然使用真机测试的,效果完全符合我们的预期。至此,本项目完结了。一个支持多点触控的ImageView做了出来。

二、小结

在拖动图片的时候会与ViewPager发生冲突,因为ViewPager也会处理拖动事件。因此为了解决这个冲突,必须在ZoomImageView中添加代码:

    if(rectf.width()>getWidth()+0.01||rectf.height()>getHeight()+0.01)
            {

                //请求父类不要拦截ACTION_DOWN事件
                if(getParent() instanceof ViewPager)
                this.getParent().requestDisallowInterceptTouchEvent(true);
            }
            

注意红色代码是核心。

时间: 2024-10-26 22:10:55

(五)多点触控之兼容ViewPager的相关文章

【朝花夕拾】Android自定义View篇之(八)多点触控(上)基础知识

前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11155259.html],谢谢! 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2.0开始引入的,在现在使用的Android手机上都是支持多点触控的.本系列文章将对常见的多点触控相关的重点知识进行总结,并使用多点触控来实现一些常见的效果,从而达到将

Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能

首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能,这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2.图片缩放小于正常比例时,松手会自动回弹成正常比例 3.图片缩放大于最大比例时,松手会自动回弹成最大比例 实现图片的缩放,平移,双击缩放等基本功能的代码如下,每一行代码我都做了详细的注释 public class ZoomImageView extends ImageView implements

关于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多点触控技术实战,自由地对图片进行缩放和移动

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

android 多点触控

多点触控 1.多点触控从字面意思讲就是你用大于等于2根的手指触摸子啊手机屏幕上. Android中监听触摸事件是onTouchEvent方法,它的参数为MotionEvent,下面列举MotionEvent的一些常用的方法: getPointerCount() 获得触屏的点数. getPointerId(int pointerIndex) 返回一个触摸点的标示,pointIndex是你第几个触控点的索引 getX() 获得触屏的X坐标值 getY() 获得触屏的Y坐标值 getAction()

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

简介 作为Android开发者,我们经常需要自定义控件,比如下面我们说的实现图片的多点触控和伸缩释放,这也是由于用户已经有这样的常识了,那就是看见有图片的地方就可以点击查看大图,并且可以通过手指对图片进行伸缩和移动,如果应用没有实现这一点,那么对用户来说将会是很糟糕的体验,用户很"愤怒".所以作为Android开发者,我们的任务就是让用户"爽".哈哈哈....下面我们将通过自定义ImageView实现以上功能. 涉及技术 一.Matrix(矩阵),Android是通

Cocos2dx 多点触控

1 最容易忽略的东西,对于ios平台,须得设置glView的属性: [__glView setMultipleTouchEnabled:YES]; 2 如果调用CCLayer的方法setTouchEnabled,则不需要手动加touchDelegate,因为这个函数里会自动做这些事情. 3 如果调用CCLayer的方法setTouchEnabled,之前可以调用一个设置多点或单点的函数来控制setTouchEnable的注册行为,这个函数是: setTouchMode 4 也可以直接调用 coc

Cocos2d-x3.2 多点触控

//GameScene.h #include "cocos2d.h" USING_NS_CC; class GameScene : public cocos2d::Layer { public:     static cocos2d::Scene* createScene();          virtual bool init();          virtual void onTouchesBegan(const std::vector<Touch*>& t

Android 多点触控错误处理(java.lang.IllegalArgumentException: pointerIndex out of range)

最近做View的多点触控时,每次第一次触控事件完美运行,第二次就直接崩了,错误信息如下: 01-03 00:05:44.220 4377-4410/system_process E/AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: android.ui java.lang.IllegalArgumentException: pointerIndex out of range at android.view.MotionEvent.nat