“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情”
ImageView是一个最基本的控件,使用的也比较多,但扪心自问时,却无法回答自己是否真的了解它,直到最近用的比较多,才决定抽空研究一下,原来涉及的东西竟然这么多,以下即为对ImageView的了解
1 概述
Displays image resources, for example Bitmap or Drawable resources. ImageView is also commonly used to apply tints to an image and handle image scaling.
在源码的开头中,介绍了ImageView的作用:显示图像资源,例如Bitmap 或 Drawable 资源。ImageView还通常用于将色调应用于图像和处理图像缩放。
下面为ImageView通用的一个示例
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/my_image"
android:contentDescription="@string/my_image_description"
/>
</LinearLayout>
2 使用
ImageView是一个最常用的控件,使用的范围非常广泛,从美学的角度,目前为了美观,通常UI要求显示的都是带圆角的ImageView,目前常用的方式有两种:
- CardView
- 自定义View
2.1 CardView
public class CardView extends FrameLayout {
CardView 是android原生的控件,可以满足圆角的要求,但也有一个缺点,就是圆角没有那么平滑
2.2 自定义View
class RoundImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet?,
defStyle: Int = 0
) : AppCompatImageView(context, attrs, defStyle) {
if (mRoundRect == null) {
mRoundRect = RectF(0F, 0F, width.toFloat(), height.toFloat())
}
if (mHasBorder) {
val innerPath = instance!!.getPath(
mRoundRect!!, mBorderRadius.toFloat()
)
canvas.drawPath(innerPath, mBitmapPaint)
if (isSelected) {
val selectedOutPath = instance!!
.getPath(
mSelectedOuterRect!!,
mBorderRadius - mSelectedOutlineWidth / NUMBER_FLOAT_TWO
)
canvas.drawPath(selectedOutPath, mSelectedOutCircle)
}
if (!isSelected || isSelected && mSelectedOutCircle.alpha < 255) {
// Draw default outline if selected outline is not drawn,
// or selected outline has alpha.
val outPath = instance!!
.getPath(
mOuterRect!!,
mBorderRadius - mDefaultOutlineWidth / NUMBER_FLOAT_TWO
)
canvas.drawPath(outPath, mOutCircle)
}
通过自定义View,可以实现自己想要的圆角类型和点击动画
3 ImageView 与 Matrix
查看ImageView源码,不得不提的就是八种ScaleType,从命名可知又分为了三类:
- 以matrix开头,关于matrix,建议先记下矩阵位置顺序
public static final int MSCALE_X = 0; //!< use with getValues/setValues
public static final int MSKEW_X = 1; //!< use with getValues/setValues
public static final int MTRANS_X = 2; //!< use with getValues/setValues
public static final int MSKEW_Y = 3; //!< use with getValues/setValues
public static final int MSCALE_Y = 4; //!< use with getValues/setValues
public static final int MTRANS_Y = 5; //!< use with getValues/setValues
public static final int MPERSP_0 = 6; //!< use with getValues/setValues
public static final int MPERSP_1 = 7; //!< use with getValues/setValues
public static final int MPERSP_2 = 8; //!< use with getValues/setValues
- 以Fit开头,fit就是优先满足一边,如果是fit_xy则两边都缩放到跟view一样大小,至于是否等比拉伸不管
- 以center开头,center顾名思义就是image在view的center
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
};
在源码中有下面这样一段定义,翻译过来就是:用于将图像边界缩放到此视图边界的选项,可见就是说ImageView中image的显示边界可以通过ScaleType来选择,从源码的初始化可知mScaleType的默认类型为ScaleType.FIT_CENTER
/**
* 用于将图像边界缩放到此视图边界的选项
* Options for scaling the bounds of an image to the bounds of this view.
*/
public enum ScaleType {
/**
* 绘制时使用图像矩阵进行缩放。
* Scale using the image matrix when drawing. The image matrix can be set using
* {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax:
* <code>android:scaleType="matrix"</code>.
*/
MATRIX (0),
/**
* Scale the image using {@link Matrix.ScaleToFit#FILL}.
* From XML, use this syntax: <code>android:scaleType="fitXY"</code>.
*/
FIT_XY (1),
/**
* Scale the image using {@link Matrix.ScaleToFit#START}.
* From XML, use this syntax: <code>android:scaleType="fitStart"</code>.
*/
FIT_START (2),
/**
* 默认类型
* Scale the image using {@link Matrix.ScaleToFit#CENTER}.
* From XML, use this syntax:
* <code>android:scaleType="fitCenter"</code>.
*/
FIT_CENTER (3),
/**
* Scale the image using {@link Matrix.ScaleToFit#END}.
* From XML, use this syntax: <code>android:scaleType="fitEnd"</code>.
*/
FIT_END (4),
/**
* Center the image in the view, but perform no scaling.
* From XML, use this syntax: <code>android:scaleType="center"</code>.
*/
CENTER (5),
/**
* 均匀缩放图像(保持图像的纵横比),以便图像的两个尺寸(宽度和高度)将相等
* 或大于视图的相应尺寸(减去填充)。然后图像在视图中居中。
* Scale the image uniformly (maintain the image's aspect ratio) so
* that both dimensions (width and height) of the image will be equal
* to or larger than the corresponding dimension of the view
* (minus padding). The image is then centered in the view.
* From XML, use this syntax: <code>android:scaleType="centerCrop"</code>.
*/
CENTER_CROP (6),
/**
* 均匀缩放图像(保持图像的纵横比),以便图像的两个尺寸(宽度和高度)将相等
* 或小于视图的相应尺寸(减去填充)。然后图像在视图中居中。
* Scale the image uniformly (maintain the image's aspect ratio) so
* that both dimensions (width and height) of the image will be equal
* to or less than the corresponding dimension of the view
* (minus padding). The image is then centered in the view.
* From XML, use this syntax: <code>android:scaleType="centerInside"</code>.
*/
CENTER_INSIDE (7);
ScaleType(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
查看调用堆栈可知,在执行setImageResource或者setImageDrawable时,会调用到configureBounds,此时会缩放image以适配view
下面讲解适配过程:
- 获取view的宽和高,然后再判断mScaleType是哪种类型
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
- 如果是FIT_XY则直接设置mDrawable的边界与view一致,否则就通过matrix进行转换,具体转换可以参考如下代码,总之它针对的是Drawable进行的操作
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(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 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(Math.round(dx), Math.round(dy));
} 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 = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 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));
}