Android:ScaleType与Matrix相关
关于ScaleType,网上介绍这个枚举对象的文章很多了,不过基本都只是介绍了它的效果。我在做可缩放移动的ImageView时,为了实现图片的缩放和拖动,需要记录图片的原始Matrix,在使用过程中发现,这个原始Matrix和ScaleType有着直接的关系,不同的ScaleType将会直接影响到Matrix的值进而影响了该自定义控件的效果。为了更好地理清两者的关系,我去阅读了ImageView的源码,在此记录整理后的个人理解。
首先简单介绍下不同的ScaleType,其实看名字就知道,Scale(比例)Type(类型),这个对象用以调整图片的比例缩放类型。不同的ScaleType影响的就是图片长与宽的不同缩放比例。
centerCrop 按比例扩大/缩小图片的size居中显示,使得图片的高等于View的高,使得图片宽等于或大于View的宽
centerInside 将图片的内容完整居中显示,使得图片按比例缩小或原来的大小(图片比View小时)使得图片宽等于或小于View的宽 (图片会完整显示)
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相关的更多相关文章
- android:scaleType="matrix"布局文件载入图片时候的显示方式
android:scaleType="center" 以原图的几何中心点和ImagView的几何中心点为基准,按图片的原来size居中显示,不缩放,当图片长/宽超过View的长/宽 ...
- android ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)
实例 <ImageView android:id="@+id/image" android:layout_width="fill_parent" andr ...
- ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)
1 imageView.setScaleType(ImageView.ScaleType.FIT_XY ); 1 这里我们重点理解ImageView的属性android:scaleType,即Imag ...
- Android ImageView的属性android:scaleType
ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType) imageView.setScaleType(Im ...
- 图片的ScaleType详解 ImageView的属性android:scaleType,
imageView.setScaleType(ImageView.ScaleType.FIT_XY ); 这里我们重点理解ImageView的属性android:scaleType,即ImageVie ...
- 转载《android:scaleType属性》
在网上查了好多资料,大致都雷同,大家都是互相抄袭的,看着很费劲,不好理解,自己总结一下,留着需要看的话来查找. 代码中的例子如下: <ImageView android:id="@+i ...
- Android中图像变换Matrix的原理、代码验证和应用(一)
第一部分 Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类.Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matri ...
- android:scaleType属性
Android:scaleType是控制图片如何resized/moved来匹对ImageView的size. ImageView.ScaleType / android:scaleType值的意义区 ...
- Android ImageButton android:scaleType
ImageView的属性android:scaleType,即 ImageView.setScaleType(ImageView.ScaleType). android:scaleType是控制图片如 ...
随机推荐
- 《Tomcat内核设计剖析》勘误表
<Tomcat内核设计剖析>勘误表 书中第95页图request部分印成了reqiest. 书中第311页两个tomcat3,其中一个应为tomcat4. 书中第5页URL应为URI. 书 ...
- 機器學習基石 机器学习基石(Machine Learning Foundations) 作业2 第10题 解答
由于前面分享的几篇博客已经把其他题的解决方法给出了链接,而这道题并没有,于是这里分享一下: 原题: 这题说白了就是求一个二维平面上的数据用决策树来分开,这就是说平面上的点只能画横竖两个线就要把所有的点 ...
- Android Kill Process
/********************************************************************** * Android Kill Process * 说明: ...
- 实战maven私有仓库三部曲之一:搭建和使用
在局域网内搭建maven私有仓库,可避免每次都从中央仓库下载公共jar包,另外将A模块作为二方库发布到私有仓库后,B模块可以很方便的引用,今天我们就来实战maven私有仓库的搭建和使用: 原文地址:h ...
- 从VS2010跳跃到VS2017
Visual Studio 配色方案 https://studiostyl.es/ C#语言新特性 C#4.0:http://www.cnblogs.com/yangqi/archive/2010/0 ...
- Windows下ruby安装和ri设置
1. ruby最强大的web框架ruby on rails .必学的.所以可以直接安装RailsInstaller. 地址:http://www.railsinstaller.org/en .安装过程 ...
- BZOJ4237 稻草人 【CDQ分治】
Description JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典. 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地.和启示中的一样,田地需要 ...
- 《DSP using MATLAB》示例Example 8.7
%% ------------------------------------------------------------------------ %% Output Info about thi ...
- [LOJ6145][2017 山东三轮集训 Day7]Easy
loj description 一棵树,每次给出\(l,r,x\),求从点\(x\)出发到达\([l,r]\)中任意一点的最短距离. sol 动态点分治. 建出点分树后,在每个节点上用以点编号为下标的 ...
- css设置图片水平及垂直居中
.box { /*非IE的主流浏览器识别的垂直居中的方法*/ display: table-cell; vertical-align:middle; /*设置水平居中*/ text-align:cen ...