zoukankan      html  css  js  c++  java
  • (四)双击放大与缩小图片

          自定义ZoomImageView实现到这里,基本上完成一大半了。在上一篇又给它添加了自由移动的功能。如果你没读过,可以点击下面的链接:
    http://www.cnblogs.com/fuly550871915/p/4940103.html

          在这篇文章中,就来实现双击放大或者缩小图片。用到的知识点就是GestureDetector,用它来监测双击事件。至于双击后怎么缩放图片,相信在前面几篇文章中,你都已经很熟悉了。但是难点是,我们要求双击后缓慢的放大或者缩小,而不是一下子就放大到或者缩小到目标值。这里就要结合线程来处理了。其实处理的逻辑也很简单:比如说放大,我们每隔一段时间,就对图片进行放大一次,然后看看是不是达到要求的放大比例了,如果达到了就终止,否则继续放大,直到达到要求为止。

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

           红色部分是新加的代码。在第438行,首先要将触摸事件传递给mGestureDetector。然后在第105行开始监听双击事件。因为双击后将缩放比例直接设置给图片,那么图片会猛然放大,不是很好的用户体验。因此在这里,采用了post一个Runnable方式,不断的去调整缩放比例,直到达到要求为止。具体的逻辑代码中很详细了,仔细看看。大体逻辑很简单。注意在这里,我们将双击的缩放临界点设置为midScale,即如果当前缩放比例比它小,那么久放大到midScale,否则就缩小到initScale。

          然后运行下程序,效果如下:

     

          实现的效果还不错吧。截止到目前,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大和缩小。功能齐全,可以使用了。在一篇文章中,将用它和ViewPager结合使用:《(五)多点触控兼容ViewPager》

  • 相关阅读:
    蓝牙基础学习
    树莓派识别TF卡内存错误导致内存可用减小解决方法
    有谁知道openwrt的系统的编译,烧录原理不吝赐教!
    树莓派显示屏输出设置
    树莓派连接摄像头做wifi视频
    Python3 安装PyUserInput从而实现鼠标点击事件
    树莓派ssh连接并使用sftp协议拷贝文件到电脑
    python tkinter 中 listbox绑定scrollbar实现自动循环滚动
    Python Tkinter学习
    代理模式
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4940193.html
Copyright © 2011-2022 走看看