写在前面 最近有了新的任务,学习的时间比以前少了不少,Java回炉的文估计是得缓缓了,不过每周一篇尽量保质保量。最近感觉我文写的有点不好,因为我写东西除非必要,不然概念性的东西我基本上都是一笔带过……最近感觉这对看我文的人好像不是很友好,恩,我决定改一改,尽量写的详细而有趣一些。
好了废话时间过了,前面也说了最近有了新任务,我现在是搞定用户信息这一块。一般来说现在用户都会有个头像什么的,光有个头像还不够,你还得能点击看个大图吧?光看个大图也不够啊,不说多的,你最起码得支持用户手势放大缩小什么的吧?当时脑海里第一个想到的是PhotoView,不过整个项目好像也只有这一块涉及到用户手势放大缩小,算了,自己实现一个吧。当然了,经常刷hongyang大神博客的我自然知道hongyang大神博客里有写过这东西 。所以趁周末有空果断刷之~
实现思路 做一个东西之前我们肯定要分析需求,分析完之后我们就可以利用我们会的,或者知道可以实现但是现在不会的去尝试解决这个需求。放大缩小图片,脑子里第一个反应就是矩阵,Android里貌似有个可以通过矩阵处理图像的东西,不过说真的,以前也没有用过几次,不过好歹有个想法了。至于让图片跟随用户手势放大缩小,肯定是需要支持手势检测了。恩,我的思路暂时就是这样了,接下来先去了解一下手势检测。
手势检测 当用户触摸屏幕时,会产生许多手势,down、up、scroll、fling等。一般情况下我们通过实现OnTouchListener是可以满足我们处理一般手势的需求的,说实话,实现手势放大缩小的ImageView是可以通过自己在OnTouch方法里面处理距离,滑动什么的去算缩放的。但是人总是要对自己好一点,如果有更简单的实现方式为什么不用呢?Android中提供了GestureDetector给程序员去判断不同的手势。另外也提供了** ScaleGestureDetector **来检测缩放手势。虽然后者很像前者的子类,但事实上并不是,后者也是一个独立的类。下面用一个简单的demo来演示一下这两者的触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 package com.example.luo_pc.view;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends AppCompatActivity implements GestureDetector .OnGestureListener , View .OnClickListener , ScaleGestureDetector .OnScaleGestureListener { GestureDetector detector = null ; ScaleGestureDetector scDetector = null ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button testGet = (Button) findViewById(R.id.bt_test_ges); Button testScges = (Button) findViewById(R.id.bt_test_scges); testGet.setOnClickListener(this ); testScges.setOnClickListener(this ); detector = new GestureDetector(this , this ); } @Override public void onClick (View v) { switch (v.getId()) { case R.id.bt_test_ges: detector = new GestureDetector(this , this ); scDetector = null ; break ; case R.id.bt_test_scges: scDetector = new ScaleGestureDetector(this , this ); detector = null ; break ; } } @Override public boolean onTouchEvent (MotionEvent me) { if (detector != null ) return detector.onTouchEvent(me); else return scDetector.onTouchEvent(me); } @Override public boolean onDown (MotionEvent arg0) { Toast.makeText(this , "onDown" , Toast.LENGTH_SHORT).show(); return false ; } @Override public boolean onFling (MotionEvent arg0, MotionEvent arg1, float arg2,float arg3) { Toast.makeText(this , "onFling" , Toast.LENGTH_SHORT).show(); return false ; } @Override public void onLongPress (MotionEvent arg0) { Toast.makeText(this , "onLongPress" , Toast.LENGTH_SHORT).show(); } @Override public boolean onScroll (MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { Toast.makeText(this , "onScroll" , Toast.LENGTH_SHORT).show(); return false ; } @Override public void onShowPress (MotionEvent arg0) { Toast.makeText(this , "onShowPress" , Toast.LENGTH_SHORT).show(); } @Override public boolean onSingleTapUp (MotionEvent arg0) { Toast.makeText(this , "onSingleTapUp" , Toast.LENGTH_SHORT).show(); return true ; } @Override public boolean onScale (ScaleGestureDetector detector) { Toast.makeText(MainActivity.this , "onScale" , Toast.LENGTH_SHORT).show(); return true ; } @Override public boolean onScaleBegin (ScaleGestureDetector detector) { Toast.makeText(MainActivity.this , "onScaleBegin" , Toast.LENGTH_SHORT).show(); return true ; } @Override public void onScaleEnd (ScaleGestureDetector detector) { Toast.makeText(MainActivity.this , "onScaleEnd" , Toast.LENGTH_SHORT).show(); } }
图方便,我将整个MainActivity搬上来了,你可以直接复制,然后加上对应的布局和导包就行了,接下来看一下运行现象。
上面测试的是GestureDetector,接下来测试一下ScaleGestureDetector
如果你想要测试更多,比如GestureDetector里另外一个接口可以把我的代码复制一下改一改就好了,这了就不作过多的赘述了,代码会说话。
Matrix 这里只对Matrix作简单的介绍。Android中Matrix是一个3 x 3的矩阵(说到矩阵都是二维的,不要看到3 x 3就想到3维去了)。先看一下Matrix的getValues和setValues方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void getValues (float [] values) { if (values.length < 9 ) { throw new ArrayIndexOutOfBoundsException(); } native_getValues(native_instance, values); } public void setValues (float [] values) { if (values.length < 9 ) { throw new ArrayIndexOutOfBoundsException(); } native_setValues(native_instance, values); }
得到或者设置一个有9个元素的数组,继续往下看发现调用的是个native修饰方法,好吧,不继续看了,了解以上也差不多够了。其内部有
Matrix的对图像的处理可分为四类基本变换: Translate 平移变换 Rotate 旋转变换 Scale 缩放变换 Skew 错切变换 从字面上理解,矩阵中的MSCALE用于处理缩放变换,MSKEW用于处理错切变换,MTRANS用于处理平移变换,MPERSP用于处理透视变换。实际中当然不能完全按照字面上的说法去理解Matrix。
从字面上理解那9个量,什么X轴缩放,什么扭曲,什么X轴偏移量,还带不认识的,没关系,我们现在做的操作比较简单,不需要用到那么多的参数。比如我们现在想设置偏移量(200,200) 我们可以
1 2 Matrix matrix = new Matrix(); martrix.postTranslate(200 ,200 );
实践 写完上面的东西,我已经差不多是个废人了…… 毕竟当年线性代数学的不咋滴,加上之前虽然有用过Matrix但是并不是很多,接下来进入喜闻乐见的实战时间。首先是不加任何限制,直接实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 package com.example.luo_pc.view.CustomView;import android.content.Context;import android.graphics.Matrix;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;import android.widget.ImageView;public class ZZoomImageView extends ImageView implements View .OnTouchListener , ScaleGestureDetector .OnScaleGestureListener { @SuppressWarnings("unused") private static final String TAG = "ZZoomImageView" ; ScaleGestureDetector scaleGestureDetector = null ; Matrix scaleMatrix = new Matrix(); public ZZoomImageView (Context context) { this (context, null ); } public ZZoomImageView (Context context, AttributeSet attrs) { this (context, attrs, 0 ); } public ZZoomImageView (Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); setScaleType(ScaleType.MATRIX); scaleGestureDetector = new ScaleGestureDetector(context, this ); this .setOnTouchListener(this ); } @Override public boolean onTouch (View v, MotionEvent event) { return scaleGestureDetector.onTouchEvent(event); } @Override public boolean onScale (ScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); if (getDrawable() == null ) return true ; scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2 , getHeight() / 2 ); setImageMatrix(scaleMatrix); return true ; } @Override public boolean onScaleBegin (ScaleGestureDetector detector) { return true ; } @Override public void onScaleEnd (ScaleGestureDetector detector) { } }
看一下跑起来是啥样的
将图片放到中心 嗯,我要是把这个用在项目里,老大要是看到了估计我就没有以后了……首先,是没有限制,可以无限缩小放大,第二是缩放中心点,默认都是ImageView中心,最后是刚开始加载出来我的图片有部分没加载,而且图片不在imageview的中心!我ImageView设置的可是俩match_parent啊。
有问题没事,我们一样一样,慢慢解决。首先是图片位置,图片位置的设定我们可以在图片加载的时候将他放到ImageView的中心去,同样在这个过程中,我们可以判断图片的大小,如果图片大于ImageView尺寸则将其大小调整至ImageView的大小。首先我们在ImageView的构造器中可能是无法获取到ImageView和图片的真实尺寸的,我们可以通过ViewTreeObserver在布局完成可以获取真实尺寸的时候完成对图片的调整。而OnGlobalLayoutListener是ViewTreeObserver的内部接口,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到。所以新增代码如下:
然后我们在此控件的onAttachedToWindow中设置监听,在onDetachedFromWindow移除这个监听:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override protected void onAttachedToWindow () { super .onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this ); } @Override @SuppressWarnings("deprecation") protected void onDetachedFromWindow () { super .onDetachedFromWindow(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { getViewTreeObserver().removeOnGlobalLayoutListener(this ); } getViewTreeObserver().removeGlobalOnLayoutListener(this ); }
最后是最重要的,在回调中对图片进行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public void onGlobalLayout () { if (!once) return ; Drawable d = getDrawable(); if (d == null ) return ; int width = getWidth(); int height = getHeight(); int imgWidth = d.getIntrinsicWidth(); int imgHeight = d.getIntrinsicHeight(); float scale = 1.0f ; if (imgWidth > width && imgHeight <= height) scale = (float ) width / imgWidth; if (imgHeight > height && imgWidth <= width) scale = (float ) height / imgHeight; if (imgWidth > width && imgHeight > height) scale = Math.min((float ) imgWidth / width, (float ) imgHeight / height); Log.e(TAG, "scale" + scale); scaleMatrix.postTranslate((width - imgWidth) / 2 , (height - imgHeight) / 2 ); scaleMatrix.postScale(scale, scale, getWidth() / 2 , getHeight() / 2 ); setImageMatrix(scaleMatrix); once = false ; }
对图片的处理核心思想就是判断图片尺寸和当前控件尺寸,图片尺寸比控件大,就对图片进行缩放处理,并且最后将图片移动至控件中心处。代码上的注释写的都很详细了,各位看官可以自行阅读。现在来看看变成啥样了
限制缩放 很好图是到中间去了,那现在的问题就是无限缩小和放大的问题。这个问题解决思路是很简单的,做个限制就行了。
嗯,新增如下几个变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static final float SCALE_MAX = 4.0f ;private float initScale = 1.0f ;float [] martixValue = new float [9 ];
上面费了那么多口水讲到的matrix的九个值啥的,终于要出现了,是不是很激动~(才怪),接下来搞个方法获取缩放比例
1 2 3 4 5 6 7 public float getScale () { scaleMatrix.getValues(martixValue); return martixValue[Matrix.MSCALE_X]; }
之后为了获取正确的初始缩放比例,在我们刚刚写的** onGlobalLayout **中加句话:
当然了,得是在获取了scale值之后再添,因为我们虽然设置了初始缩放比例,但是实际中可能因为图片大小发生了缩放行为,所以我们需要再次确定初始缩放比例。接下来就是对缩放行为进行限制了,修改onScale代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Override public boolean onScale (ScaleGestureDetector detector) { float scale = getScale(); Log.e(TAG, "matrix scale---->" + scale); float scaleFactor = detector.getScaleFactor(); Log.e(TAG, "scaleFactor---->" + scaleFactor); if (getDrawable() == null ) return true ; if ((scale < SCALE_MAX && scaleFactor > 1.0f ) || (scale > initScale && scaleFactor < 1.0f )) { if (scaleFactor * scale < initScale) scaleFactor = initScale / scale; if (scaleFactor * scale > SCALE_MAX) scaleFactor = SCALE_MAX / scale; Log.e(TAG, "scaleFactor2---->" + scaleFactor); scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2 , getHeight() / 2 ); setImageMatrix(scaleMatrix); } return true ; }
对于以上的代码,你可能会对两个scale有所疑惑,一个scale是从matrix中获得的,一个是从缩放检测中获得的。开始我看到hongyang大神的这段代码我也是有所疑惑的,但是之后我自己写了一遍,打了一下log,发现前一个在到达我们设置的最大值时,值便会固定为4,后一个值会在1左右。那么很明显前一个值是图片相对于初始尺寸的缩放,后一个是每一次缩放的实际比例。理解了这个之后便容易解决了,使用如上代码便可以限制缩放了。如果你对于缩放比例不满意,嗯,自己设置就是了,反正也不复杂。效果图就等下一个功能一起实现再放了。
以上一个简单,还算能用的缩放ImageView就完成了,现在的问题是缩放中心是控件的中心,如果我想设置缩放中心是我按下去的地方呢?很简单改一句代码:
1 2 3 4 5 scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2 , getHeight() / 2 ); scaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
但是这一改出事了……现在是能根据手势缩放中心进行缩放了,但是缩放到最小时图片位置可能发生了变化……现在还要解决的就是缩放时图片位置变化,新增如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private void checkBorderAndCenterWhenScale () { Matrix matrix = scaleMatrix; RectF rectF = new RectF(); Drawable d = getDrawable(); if (d != null ) { rectF.set(0 , 0 , d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rectF); } float deltaX = 0 ; float deltaY = 0 ; int width = getWidth(); int height = getHeight(); if (rectF.width() >= width) { if (rectF.left > 0 ) { deltaX = -rectF.left; } if (rectF.right < width) { deltaX = width - rectF.right; } } if (rectF.height() >= height) { if (rectF.top > 0 ) { deltaY = -rectF.top; } if (rectF.bottom < height) { deltaY = height - rectF.bottom; } } if (rectF.width() < width) { deltaX = width * 0.5f - rectF.right + 0.5f * rectF.width(); } if (rectF.height() < height) { deltaY = height * 0.5f - rectF.bottom + 0.5f * rectF.height(); } scaleMatrix.postTranslate(deltaX, deltaY); }
然后在onScale方法里调用以上检测的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 if ((scale < SCALE_MAX && scaleFactor > 1.0f ) || (scale > initScale && scaleFactor < 1.0f )) { if (scaleFactor * scale < initScale) scaleFactor = initScale / scale; if (scaleFactor * scale > SCALE_MAX) scaleFactor = SCALE_MAX / scale; Log.e(TAG, "scaleFactor2---->" + scaleFactor); scaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorderAndCenterWhenScale(); setImageMatrix(scaleMatrix); }
最终成型 以上代码算出初步的能用了,不过还有一点值得注意的地方,如果你在onTouch这个方法里的代码是这样的:
1 return scaleGestureDetector.onTouchEvent(event);
那么所有的事件都会被消费,因为我点到scaleGestureDetector的onTouch方法里,没看到return false的东西,所以你设置的oncilck事件之类的都没什么卵用。
对于我来说这样是不行的,因为我希望用户点击一次之后可以退出当前界面,所以你可以调用sacleGestureDetector.onTouchEvent(event)但是返回false,不消耗这个事件,让onClick来处理点击事件。当我想的很美的时候,却发现这么做虽然点击事件会被处理,而且缩放也正常,但是缩放的操作会被判断为点击事件,也就是说这么干不行了。我的脑海中第二个想到的解决方案是回调,既然系统的回调不行了,那我自己设置一个时间,在这个时间之内就是click事件,我在这个事件的回调里把当前界面退出了不就行了。实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 private ClickCloseListener c;public interface ClickCloseListener { void close () ; } public void setClickCloseListener (ClickCloseListener c) { this .c = c; } long downTime;long closeTime = 100L ;@SuppressWarnings("unused") public void setClickTime (long time) { this .closeTime = time; } @Override public boolean onTouch (View v, MotionEvent event) { scaleGestureDetector.onTouchEvent(event); if (c == null ) return true ; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downTime = System.currentTimeMillis(); break ; case MotionEvent.ACTION_UP: downTime = System.currentTimeMillis() - downTime; if (downTime < closeTime) c.close(); break ; default : break ; } return true ; }
最后看一下效果图吧~
当然了,自己搞的点击事件有点不靠谱,时间间隔设置为100ms,有点短了,你可以自己设置,不过这篇文到这里也就结束了。本来还想连什么移动一起加上,嗯,现在发现好像篇幅超出了我的控制。暂且还是算了吧~而且这个姑且也算是能用了,只不过适用的场景只是查看大图的一个单独的界面。这个简单的小东西就写到这了。完整代码就放在这把,也懒得上传github了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 package com.example.luo_pc.view.CustomView;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.os.Build;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;import android.view.ViewTreeObserver;import android.widget.ImageView;public class ZZoomImageView extends ImageView implements View .OnTouchListener , ScaleGestureDetector .OnScaleGestureListener , ViewTreeObserver .OnGlobalLayoutListener { @SuppressWarnings("unused") private static final String TAG = "ZZoomImageView" ; public static final float SCALE_MAX = 4.0f ; private float initScale = 1.0f ; ScaleGestureDetector scaleGestureDetector = null ; Matrix scaleMatrix = new Matrix(); float [] martixValue = new float [9 ]; public ZZoomImageView (Context context) { this (context, null ); } public ZZoomImageView (Context context, AttributeSet attrs) { this (context, attrs, 0 ); } public ZZoomImageView (Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); setScaleType(ScaleType.MATRIX); scaleGestureDetector = new ScaleGestureDetector(context, this ); this .setOnTouchListener(this ); } @Override protected void onAttachedToWindow () { super .onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this ); } @Override @SuppressWarnings("deprecation") protected void onDetachedFromWindow () { super .onDetachedFromWindow(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { getViewTreeObserver().removeOnGlobalLayoutListener(this ); } getViewTreeObserver().removeGlobalOnLayoutListener(this ); } public float getScale () { scaleMatrix.getValues(martixValue); return martixValue[Matrix.MSCALE_X]; } private void checkBorderAndCenterWhenScale () { Matrix matrix = scaleMatrix; RectF rectF = new RectF(); Drawable d = getDrawable(); if (d != null ) { rectF.set(0 , 0 , d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rectF); } float deltaX = 0 ; float deltaY = 0 ; int width = getWidth(); int height = getHeight(); if (rectF.width() >= width) { if (rectF.left > 0 ) { deltaX = -rectF.left; } if (rectF.right < width) { deltaX = width - rectF.right; } } if (rectF.height() >= height) { if (rectF.top > 0 ) { deltaY = -rectF.top; } if (rectF.bottom < height) { deltaY = height - rectF.bottom; } } if (rectF.width() < width) { deltaX = width * 0.5f - rectF.right + 0.5f * rectF.width(); } if (rectF.height() < height) { deltaY = height * 0.5f - rectF.bottom + 0.5f * rectF.height(); } scaleMatrix.postTranslate(deltaX, deltaY); } private ClickCloseListener c; public interface ClickCloseListener { void close () ; } public void setClickCloseListener (ClickCloseListener c) { this .c = c; } long downTime; long closeTime = 100L ; @SuppressWarnings("unused") public void setClickTime (long time) { this .closeTime = time; } @Override public boolean onTouch (View v, MotionEvent event) { scaleGestureDetector.onTouchEvent(event); if (c == null ) return true ; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downTime = System.currentTimeMillis(); break ; case MotionEvent.ACTION_UP: downTime = System.currentTimeMillis() - downTime; if (downTime < closeTime) c.close(); break ; default : break ; } return true ; } @Override public boolean onScale (ScaleGestureDetector detector) { float scale = getScale(); float scaleFactor = detector.getScaleFactor(); if (getDrawable() == null ) return true ; if ((scale < SCALE_MAX && scaleFactor > 1.0f ) || (scale > initScale && scaleFactor < 1.0f )) { if (scaleFactor * scale < initScale) scaleFactor = initScale / scale; if (scaleFactor * scale > SCALE_MAX) scaleFactor = SCALE_MAX / scale; Log.e(TAG, "scaleFactor2---->" + scaleFactor); scaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorderAndCenterWhenScale(); setImageMatrix(scaleMatrix); } return true ; } @Override public boolean onScaleBegin (ScaleGestureDetector detector) { return true ; } @Override public void onScaleEnd (ScaleGestureDetector detector) { } boolean once = true ; @Override public void onGlobalLayout () { if (!once) return ; Drawable d = getDrawable(); if (d == null ) return ; int width = getWidth(); int height = getHeight(); int imgWidth = d.getIntrinsicWidth(); int imgHeight = d.getIntrinsicHeight(); float scale = 1.0f ; if (imgWidth > width && imgHeight <= height) scale = (float ) width / imgWidth; if (imgHeight > height && imgWidth <= width) scale = (float ) height / imgHeight; if (imgWidth > width && imgHeight > height) scale = Math.min((float ) imgWidth / width, (float ) imgHeight / height); initScale = scale; scaleMatrix.postTranslate((width - imgWidth) / 2 , (height - imgHeight) / 2 ); scaleMatrix.postScale(scale, scale, getWidth() / 2 , getHeight() / 2 ); setImageMatrix(scaleMatrix); once = false ; } }
参考资料: