源码分析ImageView中ScaleType设置fitCenter和centerInside的区别
Posted 碎格子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析ImageView中ScaleType设置fitCenter和centerInside的区别相关的知识,希望对你有一定的参考价值。
在设置ImageView的scaleType时,有时常常搞不清fitCenter和centerInside的区别,网上资料说的也很模糊,今天就从底层源码看看这俩类型的区别。
查看ImageView的代码,发现有这样的一个ScaleType数组,里面存放了我们所有的可设置的type枚举。
private static final ScaleType[] sScaleTypeArray =
ScaleType.MATRIX,
ScaleType.FIT_XY,
ScaleType.FIT_START,
ScaleType.FIT_CENTER,
ScaleType.FIT_END,
ScaleType.CENTER,
ScaleType.CENTER_CROP,
ScaleType.CENTER_INSIDE
;
centerInside
搜索ScaleType.CENTER_INSIDE,找到使用枚举处的方法。为了便于理解,我将其他类型的代码都注释掉了,只留下我们要分析的代码。
ImageView.java
private void configureBounds()
if (mDrawable == null || !mHaveFrame)
return;
final int dwidth = mDrawableWidth; //图片的宽度
final int dheight = mDrawableHeight; //图片的高度
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight; //view的宽度
final int vheight = getHeight() - mPaddingTop - mPaddingBottom; //view的高度
...
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType)
...
else
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);
if (ScaleType.MATRIX == mScaleType)
...
else if (fits)
...
else if (ScaleType.CENTER == mScaleType)
...
else if (ScaleType.CENTER_CROP == mScaleType)
...
else if (ScaleType.CENTER_INSIDE == mScaleType)
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) //如果图片本身宽高均小于view的宽高,则无需进行缩放
scale = 1.0f;
else //在宽度比和高度比中取较小的值作为缩放比例
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
dx = Math.round((vwidth - dwidth * scale) * 0.5f); //x轴平移距离
dy = Math.round((vheight - dheight * scale) * 0.5f); //y轴平移距离
mDrawMatrix.setScale(scale, scale); //设置缩放
mDrawMatrix.postTranslate(dx, dy); //平移
else
...
fitCenter
针对FIT有关的类型,ImageView里定义了一个专门存放Fit类型的数组:
ImageView.java
private static final Matrix.ScaleToFit[] sS2FArray =
Matrix.ScaleToFit.FILL,
Matrix.ScaleToFit.START,
Matrix.ScaleToFit.CENTER,
Matrix.ScaleToFit.END
;
其实从定义可以看出,实现fitCenter的方式是用矩阵方式存改变量(平移量,缩放量等)
之前configureBounds()方法省略的代码里,else处理的就是fit相关的几个类型,所以fitCenter是走的else逻辑
ImageView.java
else if (ScaleType.CENTER_INSIDE == mScaleType)
...
else
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
查看setRectToRect方法
Matrix.java
@FastNative
private static native boolean nSetRectToRect(long nObject,
RectF src, RectF dst, int stf);
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)
if (dst == null || src == null)
throw new NullPointerException();
return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
nSetRectToRect定义的是native方法,实际上是通过Matrix_Delegate类实现这个方法的,找到实际使用CENTER的地方:
Matrix_Delegate.java
@LayoutlibDelegate
/*package*/ static boolean nSetRectToRect(long native_object, RectF src,
RectF dst, int stf) //src是源图片的RectF,dst是view的RectF
Matrix_Delegate d = sManager.getDelegate(native_object);
...
if (dst.isEmpty())
...
else
float tx, sx = dst.width() / src.width(); //view宽度和原图片宽度比
float ty, sy = dst.height() / src.height(); //view宽度和原图片高度比
boolean xLarger = false; //宽度比值是否更大
if (stf != ScaleToFit.FILL.nativeInt)
if (sx > sy) //宽度比大于高度比,按更小的值设置缩放比
xLarger = true;
sx = sy;
else
sy = sx;
tx = dst.left - src.left * sx; //源图片缩放后left位置差值
ty = dst.top - src.top * sy;
if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt)
float diff;
if (xLarger) //宽度比更大(x比更大)
diff = dst.width() - src.width() * sy; //按更小的缩放比缩放后求出宽度差
else
diff = dst.height() - src.height() * sy; //按更小的缩放比缩放后求出高度差
if (stf == ScaleToFit.CENTER.nativeInt)
diff = diff / 2;
if (xLarger)
tx += diff; //平移居中
else
ty += diff;
d.mValues[0] = sx; //x轴缩放
d.mValues[4] = sy; //y轴缩放
d.mValues[2] = tx; //x轴平移
d.mValues[5] = ty; //y轴平移
d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0; //其他不动
// shared cleanup
d.mValues[8] = 1;
return true;
上面代码看出不管源图片的宽高多少,设置fitCenter一定会以View的宽或高为基准对图片进行缩放处理。
区别
由设置图片1的例子可以看出,其实区别在于dwidth <= vwidth && dheight <= vheight这种情况下,centerInside 是不会对图片进行缩放,会保持图片原来的大小进行居中展示,而fitCenter一定会按宽度比或者高度比中更小的比例进行缩放,使图片的宽或高等于view的宽或高。
以上是关于源码分析ImageView中ScaleType设置fitCenter和centerInside的区别的主要内容,如果未能解决你的问题,请参考以下文章
ImageView的源码解读,以及几种ScaleType的分析
Android 源码系列之<一>从源码的角度深入理解ImageView的ScaleType属性