基礎(chǔ)儲備
在 ImageView 中有一個成員變量mDrawMatrix,這個變量是Matrix(矩陣)類型,我們了解一下這個Matrix類,Matrix 常見的方法有setScale(sx,sy) ,setTranslate(float dx, float dy)等方法. 在設(shè)置了mDrawMatrix的一系列方法后, 當ImageView方法里的 onDraw(canvas)執(zhí)行時候,canvas 會根據(jù)mDrawMatrix設(shè)置的值對圖片資源(bitmap)的繪制進行相應(yīng)的變換.
例如:
- 當mDrawMatrix.setTranslate(10,10)時,在執(zhí)行 ImageView 的 onDraw(canvas)方法時候,canvas 在對圖片(bitmap)進行繪制的時候, 會將圖片資源在在 x,y 軸兩個方向上分別偏移10px.
- 當mDrawMatrix.setScale(1.5f,1.5f)時,canvas 在對圖片(bitmap)進行繪制的時候, 會將圖片資源放大1.5f 倍
基礎(chǔ)儲備部分一定要理解,也是下面文章的核心部分
Tips
在我們實驗的時候我們要考慮圖片與 ImageView的以下幾種關(guān)系:
- 圖片的寬高大于 ImageView 的寬高
- 圖片的寬高小于 ImageView 的寬高
- 圖片的寬大于 ImageView 的寬,圖片的高小于 ImageView 的高
- 圖片的高大于 ImageView 的高,圖片的寬小于 ImageView 的寬
不同的情況得到的結(jié)果也可能會不一樣.
setScaleType()是什么?
在平常開發(fā)中我們加載得到圖片有可能與 ImageView 的大小不一樣(如果 ImageView 大小是固定的),圖片在 ImageView中應(yīng)該怎么顯示?這就需要我們使用setScaleType(ScaleType scaleType)去設(shè)置,該方法主要用于調(diào)整圖片大小來適應(yīng) ImageView 的大小,ScaleType 是 ImageView 的一個內(nèi)部枚舉類型,通過源碼該枚舉類有如下八個類型:
public enum ScaleType {
MATRIX (0),
FIT_XY (1),
FIT_START (2),
FIT_CENTER (3),
FIT_END (4),
CENTER (5),
CENTER_CROP (6),
CENTER_INSIDE (7);
ScaleType(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
ImageView的默認的ScaleType 是什么?
在 ImageView 的構(gòu)造方法中調(diào)用了initImageView()方法,在該方法中我們看到 ImageView 的 mScaleType 的默認值是 FIT_CENTER
private void initImageView() {
mMatrix = new Matrix();
mScaleType = ScaleType.FIT_CENTER;
........
}
setImageDrawable()執(zhí)行過程
setScaleType()方法設(shè)置的ScaleType值不同, 可以得到圖片在 ImageView 中不同的顯示結(jié)果.當為 ImageView設(shè)置一個圖片資源的時候使用setImageDrawable(drawable)
public void setImageDrawable(@Nullable Drawable drawable) {
......
updateDrawable(drawable);
......
//調(diào)用 onDraw()方法,重新繪制
invalidate();
}
setImageDrawable()方法調(diào)用了updateDrawable(drawable)方法,保存了 drawable 的寬和高.
private void updateDrawable(Drawable d) {
....
mDrawable = d;
if (d != null) {
......
//mDrawable 的寬度
mDrawableWidth = d.getIntrinsicWidth();
//mDrawable 的高度
mDrawableHeight = d.getIntrinsicHeight();
......
configureBounds();
} else {
mDrawableWidth = mDrawableHeight = -1;
}
}
接下來進入configureBounds()方法
private void configureBounds() {
......
//mDrawable 寬度
int dwidth = mDrawableWidth;
//mDrawable 高度
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.
//根據(jù)不同的類型對mDrawMatrix做不同的處理
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) {
......
}else{
......
}
}
在configureBounds()方法中我們看了一系列的對mScaleType值的判斷,根據(jù)mScaleType值得不同對mDrawMatrix做一些處理.
setImageDrawable()方法中有這么一行代碼invalidate();我們知道調(diào)用invalidate()方法會重新執(zhí)行onDraw()方法.在前面我們已經(jīng)說過,在 onDraw()方法里會根據(jù)mDrawMatrix對圖片的繪制進行一系列的轉(zhuǎn)換.
- ScaleType.CENTER
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));
}
根據(jù)mScaleType == ScaleType.CENTER部分的代碼,我們可以知道canvas 在繪制圖片的時候會對圖(bitmap)片進行偏移
結(jié)論:當為 ImageView 的 mScaleType == ScaleType.CENTER 時候,圖片不放大也不縮小,圖片在控件中居中顯示,超出ImageView 部分不顯示
- ScaleType.CENTER_CROP
else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth/vwidth > dheight/vheight) {
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));
}
從代碼我們可以看到當繪制 bitmap 的時候,會對圖片先進性縮放,縮放比例為 scale,再對圖片進行偏移,偏移量為 dx,dy.
結(jié)論:圖片的寬高一定會大于或者等于 ImageView 的寬高.居中顯示,超出 ImageView 部分不顯示. 圖片的寬(高)至少有一個等于 ImageView 的寬(高)
- ScaleType.CENTER_INSIDE
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);
}
從代碼上述代碼中,根據(jù)mDrawMatrix 我們可以判斷,當 ImageView 的onDraw(canvas)方法對圖片(bitmap)進行繪制的時候,會對圖片先進性縮放,在進行位移操作.if (dwidth <= vwidth && dheight <= vheight),當圖片的寬高<ImageView的寬高時,圖片不進行縮放,圖片只居中顯示.
結(jié)論: 當mScaleType的值為ScaleType.CENTER_INSIDE時,圖片的寬高一定小于或者等于 ImageView 控件的寬高,圖片居中顯示.
ScaleType.MATRIX
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
}
從mScaleType == ScaleType.MATRIX的代碼中,將mMatrix賦值給了mDrawMatrix.可以通過調(diào)用 ImageView 的setImageMatrix(matrix)方法對mMatrix進行賦值.因此我們對 mMatrix做了什么操作,ImageView在 onDraw()方法在對圖片進行繪制的時候就會對圖片進行什么操作.-
Other (一般值為ScaleType.FIT....)
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)); }
當mScaleType是ScaleType.FIT.... 類型的時候,系統(tǒng)并沒有直接顯式mDrawMatrix進行操作,而是調(diào)用了setRectToRect(RectF src, RectF dst, ScaleToFit stf)方法
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { return native_setRectToRect(native_instance, src, dst, stf.nativeInt); }
在該方法中本地方法native_setRectToRect(native_instance, src, dst, stf.nativeInt);因此我們可以猜想可能是在本地方法中對mDrawMatrix做了一系列操作.下面給出ScaleType.FIT...結(jié)論
- FIT_START
結(jié)論:圖片的寬(高)一定小于或等于 ImageView 的寬高,圖片的寬(高)至少有一個等于ImageView的寬(高),圖片顯示在 ImageView 的左(上) - FIT_CENTER
結(jié)論:圖片的寬(高)一定小于或等于 ImageView 的寬高,圖片的寬(高)至少有一個等于ImageView的寬(高),圖片居中顯示在 ImageView 中 - FIT_END
結(jié)論:圖片的寬(高)一定小于或等于 ImageView 的寬高,圖片的寬(高)至少有一個等于ImageView的寬(高),圖片顯示在 ImageView 的右(下)
總結(jié)
本片文章中并沒有列舉例子取驗證我們的結(jié)果.如果想要很好的了解,需要用戶自己動手根據(jù)結(jié)論去驗證,根據(jù)源碼自己理解一下,當自己親身試驗一遍后,相信肯定會有所收獲.