关于ScaleType,网上介绍这个枚举对象的文章很多了,不过基本都只是介绍了它的效果。我在做可缩放移动的ImageView时,为了实现图片的缩放和拖动,需要记录图片的原始Matrix,在使用过程中发现,这个原始Matrix和ScaleType有着直接的关系,不同的ScaleType将会直接影响到Matrix的值进而影响了该自定义控件的效果。为了更好地理清两者的关系,我去阅读了ImageView的源码,在此记录整理后的个人理解。

首先简单介绍下不同的ScaleType,其实看名字就知道,Scale(比例)Type(类型),这个对象用以调整图片的比例缩放类型。不同的ScaleType影响的就是图片长与宽的不同缩放比例。

matrix 用矩阵来绘制(从左上角起始的矩阵区域)
 
fitXY  把图片不按比例扩大/缩小到View的大小显示(确保图片会完整显示,并充满View)
 
fitStart  把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置(图片会完整显示)
 
fitCenter  把图片按比例扩大/缩小到View的宽度,居中显示(图片会完整显示)
 
fitEnd   把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置(图片会完整显示)
center  按图片的原来size居中显示,当图片宽超过View的宽,则截取图片的居中部分显示,当图片宽小于View的宽,则图片居中显示
 
centerCrop  按比例扩大/缩小图片的size居中显示,使得图片的高等于View的高,使得图片宽等于或大于View的宽
 
centerInside  将图片的内容完整居中显示,使得图片按比例缩小或原来的大小(图片比View小时)使得图片宽等于或小于View的宽 (图片会完整显示)
 
我们知道,可以使用getImageMatrix方法获取ImageView中图片的Matrix对象,事实上,ScaleType影响的也正是该Matrix对象。在ImageView源码中,configureBounds用以处理Matrix和ScaleType的关系。接下去通过阅读源码来解释下为什么会实现上述显示效果。
 
configureBounds源码如下:
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
} int dwidth = mDrawableWidth;
int dheight = mDrawableHeight; int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
int vheight = getHeight() - mPaddingTop - mPaddingBottom; boolean fits = (dwidth < 0 || vwidth == dwidth) &&
(dheight < 0 || vheight == dheight); if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} 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) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),
(int) ((vheight - dheight) * 0.5f + 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix; float scale;
float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
} mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy; if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
} dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}

分段阅读:

int dwidth = mDrawableWidth;
int dheight = mDrawableHeight; int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
int vheight = getHeight() - mPaddingTop - mPaddingBottom; boolean fits = (dwidth < 0 || vwidth == dwidth) &&
(dheight < 0 || vheight == dheight);

1.在这一步中,获取了图片对象(即Drawable对象,ImageView实际显示的是Drawable对象)的宽度和高度,存放在dwidth和dheight中。同时,获取了ImageView控件的显示区域的宽度和高度,存放在vwidth和vheight中。

if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {

2.当图片的大小未知或者ScaleType为FIT_XY时,将drawable的边界设置为控件的大小,并且将图片matrix对象设置为null。setBounds方法用以设置Drawable绘制区域的大小,是一个矩形对象。在ImageView的onDraw方法中,最终会将Drawable表示的图片绘制到该区域上。在这里,就是将图片绘制到ImageView大小的区域上,也就是所谓的图片完整显示,填充满ImageView。注意此处并未对Matrix对象进行操作,而是直接设置为null,所以当ImageView的ScaleType为FIT_XY时,getImageMatrix将获取到一个初始Matrix对象,getImageMatrix方法如下。

 public Matrix getImageMatrix() {
if (mDrawMatrix == null) {
return new Matrix(Matrix.IDENTITY_MATRIX);
}
return mDrawMatrix;
}
else {
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);

3.当ScaleType不为FIT_XY并且图片宽高不为0,就会把drawable的绘制区域设成图片实际大小。注:此处的实际大小不是指文件大小,而是指源文件大小和屏幕分辨率计算后的结果。

 if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
}

4.接下去判断ScaleType是否为MATRIX,如果为MATRIX类型,就会把当前绘制用的的Matrix赋值为mMatrix,mMatrix是通过setImageMatrix方法赋值的。这之后还有一个if(fits)判断,假如fits为true,表示图片大小等于ImageView大小,也不需要进行缩放操作了。

else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),
(int) ((vheight - dheight) * 0.5f + 0.5f));
}

5.当ScaleType类型为CENTER时,实际操作是将图片进行右移和下移操作,移动的数值分别为为ImageView宽度、高度与图片宽度、高度差值的一半,这样就达到了居中显示的效果。注意,CENTER不进行缩放操作只进行位移操作,所以图片的显示大小并未改变,当图片大于控件时,只显示居中部分,大于控件的部分未显示。

else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix; float scale;
float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
} mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
}

6.在CENTER_CROP中,首先对dwidth * vheight > vwidth * dheight进行了一个判断,这个判断是什么意思呢?其实换成这样比较容易理解:dwidth/vwidth>dheight/vheight,即判断到底是图片的宽度比较接近控件宽度还是图片高度比较控件高度,最终会取相差较大的项,将其放大至控件对应的值。而另外一项将超出控件大小。之后,对其进行位移,使超出控件大小的一项居中显示。注:当图片大于控件时,是将超出更少的项进行缩放。CROP的目的在于对图片宽高进行变换,使其中一项和控件的值相等,另外一项大于控件的值。

else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy; if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
} dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
}

7.当为CENTER_INSIDE时,若图片高宽均小于控件高宽,则不进行缩放只进行偏移,偏移方式跟其他情况相同。否则,将根据图片和控件的宽高比例差距更大的一项进行缩放,缩放的结果就是其中一项和控件相同,另外一项小于控件值。这个刚好和CENTER_CROP相反。

else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}

8.最后三种FIT_CENTER、FIT_START、FIT_END都是调用setRectToRect获取缩放偏移矩阵。setRectTorect返回一个Matrix,该Matrix表示从矩形mTempSrc到mTemDst的变换矩阵,根据第三个参数Matrix.ScaleToFit来确定缩放选项。

Matrix.ScaleToFit定义了四种选项:

CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。
END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。END提供右下对齐。
FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。
START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。START提供左上对齐。

ScaleType的FIT_CENTER、FIT_START、FIT_END分别对应于这里的CENTER、END、START。

总结下就一句话:ScaleType本质上是影响了ImageView中的mDrawMatrix对象,该对象用以在绘制时对Drawable对象进行矩阵转换。

Android:ScaleType与Matrix相关的更多相关文章

  1. android:scaleType=&quot;matrix&quot;布局文件载入图片时候的显示方式

    android:scaleType="center" 以原图的几何中心点和ImagView的几何中心点为基准,按图片的原来size居中显示,不缩放,当图片长/宽超过View的长/宽 ...

  2. android ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)

    实例 <ImageView android:id="@+id/image" android:layout_width="fill_parent" andr ...

  3. ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)

    1 imageView.setScaleType(ImageView.ScaleType.FIT_XY ); 1 这里我们重点理解ImageView的属性android:scaleType,即Imag ...

  4. Android ImageView的属性android:scaleType

    ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType) imageView.setScaleType(Im ...

  5. 图片的ScaleType详解 ImageView的属性android:scaleType,

    imageView.setScaleType(ImageView.ScaleType.FIT_XY ); 这里我们重点理解ImageView的属性android:scaleType,即ImageVie ...

  6. 转载《android:scaleType属性》

    在网上查了好多资料,大致都雷同,大家都是互相抄袭的,看着很费劲,不好理解,自己总结一下,留着需要看的话来查找. 代码中的例子如下: <ImageView android:id="@+i ...

  7. Android中图像变换Matrix的原理、代码验证和应用(一)

    第一部分 Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类.Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matri ...

  8. android:scaleType属性

    Android:scaleType是控制图片如何resized/moved来匹对ImageView的size. ImageView.ScaleType / android:scaleType值的意义区 ...

  9. Android ImageButton android:scaleType

    ImageView的属性android:scaleType,即 ImageView.setScaleType(ImageView.ScaleType). android:scaleType是控制图片如 ...

随机推荐

  1. (转)Pig 重写加载函数和存储函数UDF

    pig自带的pigstorage不能指定行分隔符,所以自己重写了一个简单的UDF类,可以指定列和行的分隔符,之前研究过的简单的, http://blog.csdn.net/ruishenh/artic ...

  2. Android内存优化(三)避免可控的内存泄漏

    相关文章 Android性能优化系列 Java虚拟机系列 前言 内存泄漏向来都是内存优化的重点,它如同幽灵一般存于我们的应用当中,有时它不会现身,但一旦现身就会让你头疼不已.因此,如何避免.发现和解决 ...

  3. java面试题大纲

    跳槽时时刻刻都在发生,但是我建议大家跳槽之前,先想清楚为什么要跳槽.切不可跟风,看到同事一个个都走了,自己也盲目的开始面试起来(期间也没有准备充分),到底是因为技术原因(影响自己的发展,偏移自己规划的 ...

  4. 网络编程 socket编程 - Asyncsocke

    简单的聊天程序:http://blog.csdn.net/chang6520/article/details/7967662 iPhone的标准推荐是CFNetwork 库编程,其封装好的开源库是 c ...

  5. ios逆向工程-动态分析

    先说说为什么要分析应用吧,如果你想从一个ios应用中获取有用的信息,或者你想修改该应用的一些功能,前提当然是要先知道该app的逻辑和结构了. 动态分享工具比较少,我们先分析个简单的,全民工具Cycri ...

  6. BZOJ - 4318: OSU! (期望DP&Attention)

    Description osu 是一款群众喜闻乐见的休闲软件.  我们可以把osu的规则简化与改编成以下的样子:  一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1 ...

  7. Java并发--volatile详情

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

  8. javascript中typeof用法

    JS中的变量是松散类型(即弱类型)的,可以用来保存任何类型的数据. typeof 可以用来检测给定变量的数据类型,可能的返回值: 1. 'undefined' --- 这个值未定义: 2. 'bool ...

  9. BZOJ4561 JLoi2016 圆的异或并 【扫描线】【set】*

    BZOJ4561 JLoi2016 圆的异或并 Description 在平面直角坐标系中给定N个圆.已知这些圆两两没有交点,即两圆的关系只存在相离和包含.求这些圆的异或面积并.异或面积并为:当一片区 ...

  10. istio 安装试用

    1. 命令行工具 curl -L https://git.io/getIstio | sh - 2. 环境变量配置 export PATH=$PWD/bin:$PATH 3. RBAC 检验 kube ...