(二)弥补图片自由缩放出现的间隙

在上一篇文章中,初步实现了自定义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;
    }

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

时间: 2024-09-30 03:19:26

(二)弥补图片自由缩放出现的间隙的相关文章

左图有文本,图片自由缩放

核心点有两个,大盒子设置box-sizing:border-box,这样的话设置padding值后就不会有横向滚动条 然后就是图片左浮动,文本有浮动,这样就能让文本和图片之间有间隔. 由于采用百分比布局,图片就能在窗口变化的时候,自由缩放了. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</

Android图片旋转,缩放,位移,倾斜,对称完整示例(二)——Bitmap.createBitmap()和Matrix

MainActivity如下: package cc.c; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.widget.ImageView; /** * Demo描述: * 利用B

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

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

[jquery] 图片热区随图片大小自由缩放

在图片上直接画出带超级链接热区元素map和area相信大家并不陌生,Dreamweaver等网页制作软件都有直接在图片上绘制带超级链接的热区工具,但是直接绘制的热区是不能随着图片自适应放大和缩小的,现在很多网页对浏览器分辨率兼容性要求很高,多数都是用的百分比来定义图片的尺寸,希望图片能随着分辨率的不同,显示设备的不同,自适应的显示最佳效果,这种情况下如何定义图片热区的尺寸可以随着图片一起变化呢? 这是我今天遇到的问题,问了百度个把小时也没有找到答案,忽悠人的错误答案也不少.热区的范围和坐标主要是

Android实现对图片的缩放、剪切、旋转、存储

Android实现对图片的缩放.剪切.旋转.存储 一.问题描述 在开发中,当我们需要的有一张大图片同时还需要一些小图片时,我们只需要通过代码对此图片进行不同比例的缩放即可,这样大大节约资源,减小了安装包的尺寸 .除缩放外,我们还经常对图片进行其他操作如裁剪.旋转.存储等. 这样我们可以编写对于图片进行处理的通用组件,方便开发.下面就分享一下对图片进行处理的组件BitmapUtil,案例界面: 二.技术点描述 1.通过BitmapFactory取得Bitmap Bitmap bm=BitmapFa

【Android】21.4 图片动画缩放示例

分类:C#.Android.VS2015: 创建日期:2016-03-21 一.简介 该例子演示如何动画缩放图片,实现类似"点击看大图"的效果. 二.示例 1.运行截图    2.设计步骤 (1)添加图片 在Resources/no-dpi文件夹下添加4张图片(2个缩略图,2个大图). (2)添加ch2104MyImageButton.cs using Android.Content; using Android.Widget; using System.Drawing; using

iOS transform解决连续多次旋转缩放,实现图片旋转缩放效果

一.需求 实现imageView的缩放旋转效果,一般有两种方式: 1.底层加scrollview,利用scrollview的属性实现. 2.利用手势,捏合手势.旋转手势等. 这里我选择的第二种:手势实现. 二.问题描述 一般手势处理后,对imageView进行transform处理,但我发现,每次获取手势再处理时,都会覆盖上一次的transform,从而达不到连续手势处理的效果. 比如: 我先放大一倍,再用手势放大,会发现图片会先回到原位,再放大,没有在第一次的放大位置基础继续方法,这不是我想要

[转]OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

[OpenCV入门教程之十三]OpenCV图像金字塔:高斯金字塔.拉普拉斯金字塔与图片尺寸缩放 2014-05-18 18:58 36007人阅读 评论(54) 收藏 举报 本文章已收录于:  OpenCV知识库 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http

【OpenCV入门教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 这篇文章里,我们将一起探讨图像金字塔的一