zoukankan      html  css  js  c++  java
  • (二)弥补图片自由缩放出现的间隙

          在上一篇文章中,初步实现了自定义ImageView的多点触控。但是从最终效果来看,却发现自由缩放时,图片与屏幕出现了空白间隙,这当然是让人十分厌恶的。在这篇文章中,就来解决这个问题。如果你还没读过上一篇文章,可以点击下面的链接:

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

          先贴出上一篇文章所实现的最终效果图吧,如下:

          从上图中,可以看到,图片缩放后,与屏幕之间形成了很大的空白间隙。现在来解决这个问题,主要是两个方面:

    (1)图片自由缩放后,它的中心点不应该移动,即依旧是屏幕的中心点。
    (2)图片自由缩放后,不应该与屏幕有空白间隙,主要是指宽度上的间隙。

         

    一、分析

          下面就从这两个方面着手进行分析。那么缩放后,与屏幕的宽高相比较,有几种情况呢?很简单,只有四种,缩放后的宽比屏幕的宽要大,缩放的宽比屏幕的宽要小,缩放后的高度比屏幕的高要大,缩放后的高比屏幕的高要下。其实仔细想想,因为我们前面写代码时,最下缩放比例都要求宽度为屏幕的宽,所以缩放后的宽度是不可能比屏幕宽的,不过为了完整,我们写上也无妨。以缩放后的宽比屏幕的宽要大为例,仔细分析,看下面一张图片:

         如果缩放后,图片为图示A的状态,此时其宽度大于屏幕宽度,与屏幕右侧出现了间隙,那么它应该往右移动,而移动的距离就是:屏幕宽度与右坐标的差值。同理,如果缩放后为B的状态,则图片应该左移,移动的距离应该是:0与图片左坐标的差值。同样的道理,如果图片缩放后比屏幕高,你应该也会分析了。就是将宽度改为了高度而已。

        再看一张图片:

          缩放后,A,B,C,D四种情况已经在上面讨论过,这样子只要缩放后出现上面四种情况,我们对其进行相应移动,图片的中心点就会依旧与原来的图片中心点重合,即与屏幕中心点重合。那么如果是E那种情况呢?针对情况E,再画一张图。如下:

          从图上可以看出,显然此时需要移动的距离是,x方向就是黄线的长度,可以这样子计算:屏幕宽度/2 - 图片右坐标+图片缩放后的宽度/2。在y方向上,同理,只是将宽度改为高度即可。

         好了,通过上面的分析,所有情况下应该移动的算法,我们都清楚了。下面就可以将它们写进代码中了。移动嘛,利用的依旧是Matrix.postTranslate方法了。

    二、代码

          还剩下一个问题,就是怎么获得图片根据矩阵变换后的新的坐标呢?这个问题,代码中有答案。修改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.ViewTreeObserver.OnGlobalLayoutListener;
     15 import android.view.View.OnTouchListener;
     16 import android.widget.ImageView;
     17 
     18 public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, 
     19 OnScaleGestureListener, OnTouchListener
     20 {
     21     private boolean mOnce = false;//是否执行了一次
     22     
     23     /**
     24      * 初始缩放的比例
     25      */
     26     private float initScale;
     27     /**
     28      * 缩放比例
     29      */
     30     private float midScale;
     31     /**
     32      * 可放大的最大比例
     33      */
     34     private float maxScale;
     35     /**
     36      * 缩放矩阵
     37      */
     38     private Matrix scaleMatrix;
     39     
     40     /**
     41      * 缩放的手势监控类
     42      */
     43     private ScaleGestureDetector mScaleGestureDetector;
     44     
     45 
     46     public ZoomImageView(Context context)
     47     {
     48         this(context,null);
     49     }
     50     public ZoomImageView(Context context, AttributeSet attrs) 
     51     {
     52         this(context, attrs,0);
     53 
     54     }
     55     public ZoomImageView(Context context, AttributeSet attrs, int defStyle)
     56     {
     57         super(context, attrs, defStyle);
     58         
     59         scaleMatrix = new Matrix();
     60         
     61         setScaleType(ScaleType.MATRIX);
     62         
     63         mScaleGestureDetector = new ScaleGestureDetector(context, this);
     64         //触摸回调
     65         setOnTouchListener(this);
     66         
     67     }
     68     
     69     /**
     70      * 该方法在view与window绑定时被调用,且只会被调用一次,其在view的onDraw方法之前调用
     71      */
     72     protected void onAttachedToWindow()
     73     {
     74         super.onAttachedToWindow();
     75         //注册监听器
     76         getViewTreeObserver().addOnGlobalLayoutListener(this);
     77     }
     78     
     79     /**
     80      * 该方法在view被销毁时被调用
     81      */
     82     @SuppressLint("NewApi") protected void onDetachedFromWindow() 
     83     {
     84         super.onDetachedFromWindow();
     85         //取消监听器
     86         getViewTreeObserver().removeOnGlobalLayoutListener(this);
     87     }
     88     
     89     /**
     90      * 当一个view的布局加载完成或者布局发生改变时,OnGlobalLayoutListener会监听到,调用该方法
     91      * 因此该方法可能会被多次调用,需要在合适的地方注册和取消监听器
     92      */
     93     public void onGlobalLayout() 
     94     {
     95         if(!mOnce)
     96         {
     97             //获得当前view的Drawable
     98             Drawable d = getDrawable();
     99             
    100             if(d == null)
    101             {
    102                 return;
    103             }
    104             
    105             //获得Drawable的宽和高
    106             int dw = d.getIntrinsicWidth();
    107             int dh = d.getIntrinsicHeight();
    108             
    109             //获取当前view的宽和高
    110             int width = getWidth();
    111             int height = getHeight();
    112             
    113             //缩放的比例,scale可能是缩小的比例也可能是放大的比例,看它的值是大于1还是小于1
    114             float scale = 1.0f;
    115             
    116             //如果仅仅是图片宽度比view宽度大,则应该将图片按宽度缩小
    117             if(dw>width&&dh<height)
    118             {
    119                 scale = width*1.0f/dw;
    120             }
    121             //如果图片和高度都比view的大,则应该按最小的比例缩小图片
    122             if(dw>width&&dh>height)
    123             {
    124                 scale = Math.min(width*1.0f/dw, height*1.0f/dh);
    125             }
    126             //如果图片宽度和高度都比view的要小,则应该按最小的比例放大图片
    127             if(dw<width&&dh<height)
    128             {
    129                 scale = Math.min(width*1.0f/dw, height*1.0f/dh);
    130             }
    131             //如果仅仅是高度比view的大,则按照高度缩小图片即可
    132             if(dw<width&&dh>height)
    133             {
    134                 scale = height*1.0f/dh;
    135             }
    136             
    137             //初始化缩放的比例
    138             initScale = scale;
    139             midScale = initScale*2;
    140             maxScale = initScale*4;
    141             
    142             //移动图片到达view的中心
    143             int dx = width/2 - dw/2;
    144             int dy = height/2 - dh/2;
    145             scaleMatrix.postTranslate(dx, dy);
    146             
    147             //缩放图片
    148             scaleMatrix.postScale(initScale, initScale, width/2, height/2);    
    149             
    150             setImageMatrix(scaleMatrix);
    151             mOnce = true;
    152         }
    153         
    154     }
    155     /**
    156      * 获取当前已经缩放的比例
    157      * @return  因为x方向和y方向比例相同,所以只返回x方向的缩放比例即可
    158      */
    159     private float getDrawableScale()
    160     {
    161         
    162         float[] values = new float[9];
    163         scaleMatrix.getValues(values);
    164         
    165         return values[Matrix.MSCALE_X];
    166         
    167     }
    168 
    169     /**
    170      * 缩放手势进行时调用该方法
    171      * 
    172      * 缩放范围:initScale~maxScale
    173      */
    174     public boolean onScale(ScaleGestureDetector detector)
    175     {
    176         
    177         if(getDrawable() == null)
    178         {
    179             return true;//如果没有图片,下面的代码没有必要运行
    180         }
    181         
    182         float scale = getDrawableScale();
    183         //获取当前缩放因子
    184         float scaleFactor = detector.getScaleFactor();
    185         
    186         if((scale<maxScale&&scaleFactor>1.0f)||(scale>initScale&&scaleFactor<1.0f))
    187         {
    188             //如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
    189             if(scale*scaleFactor<initScale&&scaleFactor<1.0f)
    190             {
    191                 scaleFactor = initScale/scale;
    192             }
    193             //如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
    194             if(scale*scaleFactor>maxScale&&scaleFactor>1.0f)
    195             {
    196                 scaleFactor = maxScale/scale;
    197             }
    198             
    199 //            scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
    200             scaleMatrix.postScale(scaleFactor, scaleFactor,detector.getFocusX(), 
    201                     detector.getFocusY());
    202             
    203             checkBoderAndCenter();//处理缩放后图片边界与屏幕有间隙或者不居中的问题
    204             
    205             
    206             setImageMatrix(scaleMatrix);//千万不要忘记设置这个,我总是忘记
    207         }
    208         return true;
    209     }
    210     /**
    211      * 处理缩放后图片边界与屏幕有间隙或者不居中的问题
    212      */
    213     private void checkBoderAndCenter()
    214     {
    215        RectF rectf = getDrawableRectF();
    216        
    217        int width = getWidth();
    218        int height = getHeight();
    219        
    220        float delaX =0;
    221        float delaY = 0;
    222        
    223        if(rectf.width()>=width)
    224        {
    225            if(rectf.left>0)
    226            {
    227              delaX = - rectf.left;  
    228            }
    229            
    230            if(rectf.right<width)
    231            {
    232                delaX = width - rectf.right;
    233            }   
    234        }
    235        
    236        if(rectf.height()>=height)
    237        {
    238            if(rectf.top>0)
    239            {
    240                delaY = -rectf.top;
    241            }
    242            if(rectf.bottom<height)
    243            {
    244                delaY = height - rectf.bottom;
    245            }
    246        }
    247        
    248        if(rectf.width()<width)
    249        {
    250            delaX = width/2 - rectf.right+ rectf.width()/2;
    251        }
    252        
    253        if(rectf.height()<height)
    254        {
    255            delaY =  height/2 - rectf.bottom+ rectf.height()/2;
    256        }
    257        
    258        scaleMatrix.postTranslate(delaX, delaY);
    259     }
    260     /**
    261      * 获取图片根据矩阵变换后的四个角的坐标,即left,top,right,bottom
    262      * @return 
    263      */
    264     private RectF getDrawableRectF()
    265     {
    266         Matrix matrix = scaleMatrix;
    267         RectF rectf = new RectF();
    268         Drawable d = getDrawable();
    269         if(d != null)
    270         {
    271             
    272             rectf.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    273         }
    274         
    275         matrix.mapRect(rectf);
    276         return  rectf;
    277     }
    278     /**
    279      * 缩放手势开始时调用该方法
    280      */
    281     public boolean onScaleBegin(ScaleGestureDetector detector) 
    282     {    
    283         //返回为true,则缩放手势事件往下进行,否则到此为止,即不会执行onScale和onScaleEnd方法
    284         return true;
    285     }
    286     /**
    287      * 缩放手势完成后调用该方法
    288      */
    289     public void onScaleEnd(ScaleGestureDetector detector)
    290     {
    291         
    292         
    293     }
    294 
    295     /**
    296      * 监听触摸事件
    297      */
    298     public boolean onTouch(View v, MotionEvent event)
    299     {
    300     
    301         if(mScaleGestureDetector != null)
    302         {
    303             //将触摸事件传递给手势缩放这个类
    304             mScaleGestureDetector.onTouchEvent(event);
    305         }
    306         return true;
    307     }
    308 
    309     
    310   
    311 
    312 }

              红色部分是我们增加的核心代码。第203行,我们增加了checkBoderAndCenter方法用来处理缩放后出现间隙空白的问题。而在getDrawableRectF方法中,通过一个Rectf拿到图片缩放后的新的坐标,这个方法需要牢牢掌握。其他的代码,都是按照上面我们分析的逻辑来编写的,相信你一定能看的懂了。注意第248行到第251行的代码可要可不要,因为我们最小的缩放比例就要求宽度必须等于屏幕宽度,因此不可能比屏幕宽度小,做这个判断没意义。好了,要修改的就这么多了。现在运行下程序,看看是否真的解决问题了呢?效果如下:

          依旧是在真机上录制的gif,效果还是不错的,完美解决了这个出现空隙的问题。

    三、总结

          获得图片缩放后的坐标的方法,必须要掌握。如下:

    /**
         * 获取图片根据矩阵变换后的四个角的坐标,即left,top,right,bottom
         * @return 
         */
        private RectF getDrawableRectF()
        {
            Matrix matrix = scaleMatrix;
            RectF rectf = new RectF();
            Drawable d = getDrawable();
            if(d != null)
            {
                
                rectf.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            }
            
            matrix.mapRect(rectf);
            return  rectf;
        }

          自由缩放算是告一段落了,下面我们要实现的是图片的自由移动。保存好代码,快进入下一节吧:《(三)多点触控之自由移动缩放后的图片》

  • 相关阅读:
    docker学习笔记(2)——docker常用命令
    docker学习笔记(1)——ubuntu16.04安装docker(含如何彻底卸载docker,docker拉取镜像失败解决)
    近期计划随笔
    程序员的自我修养:链接、装载与库(一本书,估计没时间看。。。哭。。。)
    str.format 学习
    pyqt5 线程 (QThread)启动、挂起、恢复、终止问题见解
    pyqt5 在非主线程(QThread线程)中实现弹窗QMessageBox
    807 · 回文数 II
    1334 · 旋转数组
    297 · 寻找最大值
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4939954.html
Copyright © 2011-2022 走看看