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是控制图片如 ...
随机推荐
- 【转】react-native开发混合App-github开源项目
http://www.lcode.org/study-react-native-opensource-one/ http://gold.xitu.io/entry/575f498c128fe10057 ...
- [Shell]bash的良好编码实践
最好的bash脚本不仅可以工作,而且以易于理解和修改的方式编写.很多好的编码实践都是来自使用一致的变量名称和一致的编码风格.验证用户提供的参数是否正确,并检查命令是否能成功运行,以及长时间运行是否能保 ...
- 使用pipework将Docker容器配置到本地网络环境中
使用pipework将Docker容器配置到本地网络环境中 需求 在使用Docker的过程中,有时候我们会有将Docker容器配置到和主机同一网段的需求.要实现这个需求,我们只要将Docker容器和主 ...
- ScrollView中嵌套ExpandableListView
为了让SrollView中嵌套ExpandableListView,我们就得改ListView的高度,所以写一个类继承于ExpandableListview: import android.conte ...
- iOS-----AVFoundation框架的功能详解
使用AVFoundation拍照和录制视频 需要开发自定义的拍照和录制视频功能,可借助于AVFoundation框架来实现,该框架提供了大量的类来完成拍照和录制视频.主要使用如下类: AVCaptur ...
- vue中axios的深入使用
如上所示一条简单的请求数据,用到了vue中axios,promise,qs等等 这里我将vue中用到的axios进行了封装方便日后使用 先对工具类进行封装utils/axios.js: // 引入模 ...
- 【集成学习】lightgbm使用案例
github地址 #!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Created on Sat Mar 31 21: ...
- Servlet实现验证码图片(一)
Servlet实现数字字母验证码图片(一): 生成验证码图片主要用到了一个BufferedImage类,如下:
- C++ set容器
STL中的set容器的一点总结:(元素唯一,且排序) 1.关于set (头文件:<set>) C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, strin ...
- webpack 简单使用
备注: 使用yarn 结合npm 模块进行简单项目开发 1. 安装 yarn init yarn add webpack --dev yarn global add live-server 2. 添 ...