• (五)多点触控之兼容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);
                }
                

          注意红色代码是核心。

  • 相关阅读:
    [WPF VTK]三维图形开发基础(一)
    WP开发(一)
    [WPF VTK]三维图形开发基础(四)

    WIN8 下IE突然无法打开(管理员权限可打开)
    [WPF VTK]三维图形开发基础(三)
    堆排序、快排的坑
    双向链表之插入
    [WPF VTK]三维图形开发基础(二)
    [转载]Android界面设计学习日志(一)
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4940398.html
Copyright © 2020-2023  润新知