首先,以我們在xml文件中使用<ImageView>標(biāo)簽使用ImageView為例,然后在Activity中設(shè)置圖片的過程,分析ImageView的顯示圖片發(fā)生了什么梭姓。
ImageView的構(gòu)造函數(shù)
public ImageView(Context context) {
super(context);
初始化一些變量和配置
initImageView();
}
public ImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
這里也調(diào)用到了這個函數(shù)
initImageView();
// ImageView is not important by default, unless app developer overrode attribute.
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
}
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
xml中設(shè)置的圖片源
final Drawable d = a.getDrawable(R.styleable.ImageView_src);
if (d != null) {
setImageDrawable(d);
}
基線對齊
mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false);
mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1);
是否調(diào)整view邊界
setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
設(shè)置View的最大寬
setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
設(shè)置View的最大高
setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
xml中的縮放類型
final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
if (a.hasValue(R.styleable.ImageView_tint)) {
mDrawableTintList = a.getColorStateList(R.styleable.ImageView_tint);
mHasDrawableTint = true;
// Prior to L, this attribute would always set a color filter with
// blending mode SRC_ATOP. Preserve that default behavior.
mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
mHasDrawableTintMode = true;
}
if (a.hasValue(R.styleable.ImageView_tintMode)) {
mDrawableTintMode = Drawable.parseTintMode(a.getInt(
R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
mHasDrawableTintMode = true;
}
applyImageTint();
圖片透明度
final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255);
if (alpha != 255) {
setImageAlpha(alpha);
}
mCropToPadding = a.getBoolean(
R.styleable.ImageView_cropToPadding, false);
a.recycle();
//need inflate syntax/reader for matrix
}
在初始化時都用到了 initImageView();方法,看看著了方法做了什么嫩码;
private void initImageView() {
初始化了默認(rèn)矩陣
mMatrix = new Matrix();
初始化了默認(rèn)縮放類型是fit_center
mScaleType = ScaleType.FIT_CENTER;
if (!sCompatDone) {
final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N;
sCompatDone = true;
}
}
ImageView 的幾個種設(shè)置圖片的方式
我們知道誉尖,對于Imageview顯示圖片,常用的有一下幾種方式
- imaegView.setImageBitmap();
- imaegView.setImageResource();
- imaegView.setImageDrawable();
- imaegView.setImageURI();
setImageBitmap()
/**
* Sets a Bitmap as the content of this ImageView.
*
* @param bm The bitmap to set
*/
@android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
// Hacky fix to force setImageDrawable to do a full setImageDrawable
// instead of doing an object reference comparison
mDrawable = null;
if (mRecycleableBitmapDrawable == null) {
mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);
} else {
mRecycleableBitmapDrawable.setBitmap(bm);
}
setImageDrawable(mRecycleableBitmapDrawable);
}
通過源碼可知铸题,setImageBitmap()最終是把傳遞過來的bitmap轉(zhuǎn)換成一個Drawable對象铡恕,然后調(diào)用的setImageDrawable()方法,那setImageDrawable()方法又是干嘛的啊
setImageDrawable
public void setImageDrawable(Drawable drawable) {
if (mDrawable != drawable) {
資源置0
mResource = 0;
uri置null
mUri = null;
原來drawable的寬高
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
更新此ImageView顯示的drawable,此方法重要丢间,幾個設(shè)置圖像的方法都調(diào)用
updateDrawable(drawable);
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
重新布局
requestLayout();
}
重新繪制
invalidate();
}
}
由源碼可知探熔,
- 全局變量mResource設(shè)置為0,
- 全局變量mUri置為null烘挫,
- 把傳遞過來的drawable對象再傳遞到updateDrawable()法中
setImageResource()
public void setImageResource(int resId) {
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(null);
mResource = resId;
mUri = null;
resolveUri();
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
由源碼可知:
- 沒有Drawable對象诀艰,所以 updateDrawable()傳遞null
- 把傳遞過來的res對象賦值給 全局變量mResource,
- 全局變量mUri置為null饮六,
- 調(diào)用 resolveUri()
setImageURI()
public void setImageURI(Uri uri) {
if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
updateDrawable(null);
mResource = 0;
mUri = uri;
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
resolveUri();
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
}
由源碼可知:
- 全局變量mResource設(shè)置為0
- 把傳遞過來的uri對象賦值給全局變量mUri其垄,
- 由于沒有Drawable對象,所以 updateDrawable()遞null
- 調(diào)用 resolveUri()
從上可知卤橄,有兩個方法挺關(guān)鍵的绿满,一個是updateDrawable(),還有就是resolveUri(),接下來就查查這兩個方法是干嘛的
updateDrawable()更新ImageView需要顯示Drawable。
private void updateDrawable(Drawable d) {
if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
mRecycleableBitmapDrawable.setBitmap(null);
}
boolean sameDrawable = false;
if (mDrawable != null) {
sameDrawable = mDrawable == d;
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
if (!sCompatDrawableVisibilityDispatch && !sameDrawable && isAttachedToWindow()) {
mDrawable.setVisible(false, false);
}
}
賦值虽风,更新此ImageView顯示的drawable
mDrawable = d;
if (d != null) {
d.setCallback(this);
d.setLayoutDirection(getLayoutDirection());
if (d.isStateful()) {
d.setState(getDrawableState());
}
if (!sameDrawable || sCompatDrawableVisibilityDispatch) {
final boolean visible = sCompatDrawableVisibilityDispatch
? getVisibility() == VISIBLE
: isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
d.setVisible(visible, true);
}
d.setLevel(mLevel);
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
設(shè)置圖片的著色列表棒口,這里會用到顏色狀態(tài)列表ColorStateList
applyImageTint();
設(shè)置圖片的顏色過濾器寄月,即設(shè)置ColorFilter
applyColorMod();
這個重要辜膝,是指根據(jù)設(shè)置的ScaleType,使用對應(yīng)的矩陣對Drawable進行變換漾肮,以顯示合適的區(qū)域
configureBounds();
} else {
mDrawableWidth = mDrawableHeight = -1;
}
}
resolveUri()
private void resolveUri() {
if (mDrawable != null) {
return;
}
Resources rsrc = getResources();
if (rsrc == null) {
return;
}
Drawable d = null;
if (mResource != 0) {
try {
d = rsrc.getDrawable(mResource);
} catch (Exception e) {
mUri = null;
}
} else if (mUri != null) {
String scheme = mUri.getScheme();
if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
try {
ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri);
d = r.r.getDrawable(r.id);
} catch (Exception e) {
Log.w("ImageView", "Unable to open content: " + mUri, e);
}
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
InputStream stream = null;
try {
stream = mContext.getContentResolver().openInputStream(mUri);
d = Drawable.createFromStream(stream, null);
} catch (Exception e) {
Log.w("ImageView", "Unable to open content: " + mUri, e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
Log.w("ImageView", "Unable to close content: " + mUri, e);
}
}
}
} else {
d = Drawable.createFromPath(mUri.toString());
}
if (d == null) {
System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
// Don't try again.
mUri = null;
}
} else {
return;
}
updateDrawable(d);
}
通過上面的代碼可知
- 如果mResource不為null 厂抖,把mResource 轉(zhuǎn)換成一個Drawable對象,然后執(zhí)行updateDrawable()
- 如果mUri不為null克懊,就把uri轉(zhuǎn)換成一個Drawable對象忱辅,然后執(zhí)行updataDrawable()方法
所以七蜘,不管是setImageUri還是setImageDrawable或者setImageResource()或者setImageBitmap
- 首先都是把傳遞過來的對象轉(zhuǎn)換成一個Drawable對象,
- 然后把執(zhí)行updataDrawable()方法墙懂,
- 因為之前在updataDrawable 中重新設(shè)置的寬高橡卤,所以執(zhí)行requestLayout() 重新布局view
- 最后執(zhí)行invalidate()重新繪制
對于上面的updateDrawale中的三個方法:
1. applyImageTint()
ImageView在的Drawable 的著色變量
private Drawable mDrawable = null;
private BitmapDrawable mRecycleableBitmapDrawable = null;
顏色狀態(tài)列表
private ColorStateList mDrawableTintList = null;
著色模式,有16種
private PorterDuff.Mode mDrawableTintMode = null;
private boolean mHasDrawableTint = false;
private boolean mHasDrawableTintMode = false;
private void applyImageTint() {
if (mDrawable != null && (mHasDrawableTint || mHasDrawableTintMode)) {
mDrawable = mDrawable.mutate();
if (mHasDrawableTint) {
設(shè)置著色集合
mDrawable.setTintList(mDrawableTintList);
}
if (mHasDrawableTintMode) {
設(shè)置著色模式
mDrawable.setTintMode(mDrawableTintMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (mDrawable.isStateful()) {
mDrawable.setState(getDrawableState());
}
}
}
1.1 ColorStateList類
ColorStateList是顏色狀態(tài)列表類损搬,ColorStateList是從XML中定義的XML資源文件中創(chuàng)建的應(yīng)用程序資源目錄的“color”子目錄碧库。 XML文件包含一個單一的“選擇器”元素內(nèi)有多個“item”元素。
<selector xmlns:android="http://schemas.android.com/apk/res/android"/>
< item android:state_focused="true"
android:color="@color/sample_focused" />;
<item android:state_pressed="true"
android:state_enabled="false"
android:color="@color/sample_disabled_pressed" />;
<item android:state_enabled="false"
android:color="@color/sample_disabled_not_pressed" />;
<item android:color="@color/sample_default" />;
</selector/>
這定義了一組狀態(tài)規(guī)格/顏色對巧勤,其中每個狀態(tài)規(guī)范指定了一組表示視圖必須處于或不在嵌灰,顏色指定相關(guān)的顏色與該規(guī)范。
安卓控制Texview的文字顏色切換:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:color="#ff2525"></item>
<item android:color="#ff5b5b5b"></item>
</selector>
ColorStateList csl=(ColorStateList)getResources().getColorStateList(R.color.button_text);
for (int i =0;i<3;i++){
Button btn = new Button(mContext);
btn.setText("按鈕"+i);
btn.setTextColor(csl);
this.addView(btn,params);
}
2. applyColorMod()
// these are applied to the drawable
顏色過濾器
private ColorFilter mColorFilter = null;
private boolean mHasColorFilter = false;
private Xfermode mXfermode;
private int mAlpha = 255;
private final int mViewAlphaScale = 256;
private boolean mColorMod = false;
private void applyColorMod() {
// Only mutate and apply when modifications have occurred. This should
// not reset the mColorMod flag, since these filters need to be
// re-applied if the Drawable is changed.
if (mDrawable != null && mColorMod) {
mDrawable = mDrawable.mutate();
if (mHasColorFilter) {
設(shè)置顏色過濾器
mDrawable.setColorFilter(mColorFilter);
}
mDrawable.setXfermode(mXfermode);
mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
}
}
2.1 ColorFilter類的使用
ColorFilter主要用來處理顏色颅悉,這里將講解它的三個子類沽瞭,ColorMatrixColorFilter,
LightingColorFilter以及PorterDuffColorFilter。
** ColorMatrixColorFilter**
這個類主要是使用matrix也就是矩陣對顏色做運算剩瓶,矩陣的形態(tài)如下:
顏色值和該矩陣的換算關(guān)系如下:
RGB和Alpha的終值計算方法如下:
Red通道終值= a[0] * srcR + a[1] * srcG + a[2] * srcB + a[3] * srcA + a[4]
Green通道終值= a[5] * srcR + a[6] * srcG + a[7] * srcB + a[8] * srcA + a[9]
Blue通道終值= a[10] * srcR + a[11] * srcG + a[12] * srcB + a[13] * srcA + a[14]
Alpha通道終值= a[15]*srcR+a[16]*srcG + a[17] * srcB + a[18] * srcA + a[19]
備注:
srcR為原圖Red通道值驹溃,srcG為原圖Green通道值,srcB為原圖Blue通道值延曙,srcA為原圖Alpha通道值吠架。
每個通道的源值和終值都在0到255的范圍內(nèi)。即使計算結(jié)果大于255或小于0搂鲫,值都將被限制在0到255的范圍內(nèi)傍药。
看了上面的說明可能不太清晰,這里再看看例子
上面有3張圖片魂仍,其實是同一個圖片繪制的拐辽,只是使用了不同的ColorMatrixColorFilter,最后面一張是完全黑白的。
private final static float[] MATRIX = new float[] {
0.5f, 0, 0, 0, 0,
0, 0.5f, 0, 0, 0,
0, 0, 0.5f, 0, 0,
0, 0, 0, 1, 0 };
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);
canvas.drawBitmap(bitmap, 100, 0, paint);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(MATRIX);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(bitmap, 100, 1000, paint);
MATRIX中的值是0.5使得R,G,B通道的值都減半了擦酌,所以第二張圖看起來暗了很多
第三張圖使用了ColorMatrix俱诸,并且使用colorMatrix.setSaturation(0)將飽和度設(shè)置為了0,這樣一來就變?yōu)榱撕诎讏D片赊舶。
明白了上面顏色運算的規(guī)則睁搭,就可以自己更改矩陣的值從而達到想要的效果。這里舉一個示例
LightingColorFilter
LightingColorFilter是上面ColorMatrixColorFilter的一個簡化版本笼平,構(gòu)造函數(shù)也比較簡單:
public LightingColorFilter(int mul, int add)
mul代表multiply,也就是乘法
add代表加法园骆,也就是顏色偏移量
那么這里將怎么計算顏色值呢,其實跟上面矩陣的方式差不多寓调。這里兩個參數(shù)都是int類型锌唾,所以每8位被分別分解為RGB對應(yīng)的值來做乘法和加法
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);
canvas.drawBitmap(bitmap, 100, 0, paint);
LightingColorFilter filter = new LightingColorFilter(0x888888, 0x000000);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);
LightingColorFilter filter2 = new LightingColorFilter(0x888888, 0x555555);
paint.setColorFilter(filter2);
canvas.drawBitmap(bitmap, 100, 1000, paint);
看第二張圖片的ColorFilter構(gòu)造new LightingColorFilter(0x888888, 0x000000);
其中mul為0x888888,那么RGB通道對應(yīng)的mul值都為88,add為0x000000則對應(yīng)的偏移量都為0晌涕。第二張圖變暗了滋捶,基本可以看出計算方法。
color = colormul/255+add (計算結(jié)果大于等于255都指定為255)
其中color可以為RGB三種通道中的一種余黎,mul和add分別為通道對應(yīng)的值重窟。假設(shè)R通道的值就為
R=R0x88/0xff+0
0x88/0xff肯定是小于1的,所以顏色變暗了惧财。
第三張圖的mul值和第二張相同亲族,但是由于第三張圖的add值比較大,所以反而比第一張圖還亮可缚。
PorterDuffColorFilter
public PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode)
srcColor源顏色霎迫,
mode是色彩的混合模式,這里的混合模式我們在后面再詳細講解帘靡,這里簡單看一個示例知给。
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);
canvas.drawBitmap(bitmap, 100, 0, paint);
PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.MULTIPLY);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);
這里PorterDuffColorFilter的兩個參數(shù)分別是Color.BLUE以及PorterDuff.Mode.MULTIPLY
先不要糾結(jié)PorterDuff.Mode.MULTIPLY的含義,這里主要是將Color.BLUE提供的藍色和原圖進行一定的合并運算描姚。就得到了上面的效果涩赢,那么傳入不同PorterDuff.Mode值就能得到不同的效果。各個值不同的效果我們在后面的篇幅中詳細講解轩勘。
關(guān)于PorterDuff.Mode筒扒,Android系統(tǒng)一共提供了18種混排模式,在模擬器的ApiDemos/Graphics/XferModes绊寻,有張效果圖:
這張圖可以很形象的說明圖片各種混排模式下的效果花墩。其中Src代表原圖,Dst代表目標(biāo)圖澄步,兩張圖片使用不同的混排方式后冰蘑,得到的圖像是如上圖所示的。PorterDuff.Mode也提供了18種混排模式已經(jīng)算法村缸,其中比上圖多了ADD和OVERLAY兩種模式
其中Sa全稱為Source alpha表示源圖的Alpha通道祠肥;Sc全稱為Source color表示源圖的顏色;Da全稱為Destination alpha表示目標(biāo)圖的Alpha通道梯皿;Dc全稱為Destination color表示目標(biāo)圖的顏色仇箱,[...,..]前半部分計算的是結(jié)果圖像的Alpha通道值,“,”后半部分計算的是結(jié)果圖像的顏色值东羹。圖像混排后是依靠這兩個值來重新計算ARGB值的剂桥,具體計算算法,抱歉百姓,我也不知道渊额,不過不要緊,不了解計算算法也不影響我們程序員寫程序的垒拢。我們只要對照上面的apiDemo中提供的圖片就能推測出混排后的結(jié)果的旬迹,下面是在網(wǎng)上找到的漢字語言描述,感謝這位作者的總結(jié)求类。
注意:先繪制dst奔垦,再繪制src。
1.PorterDuff.Mode.CLEAR
所繪制源圖像不會提交到畫布上尸疆。
2.PorterDuff.Mode.SRC
只顯示源圖像椿猎。
3.PorterDuff.Mode.DST
只顯示目標(biāo)圖像。
4.PorterDuff.Mode.SRC_OVER
正常繪制顯示寿弱,源圖像居上顯示犯眠。
5.PorterDuff.Mode.DST_OVER
上下層都顯示。目標(biāo)圖像居上顯示症革。
6.PorterDuff.Mode.SRC_IN
取兩層繪制交集中的源圖像筐咧。
7.PorterDuff.Mode.DST_IN
取兩層繪制交集中的目標(biāo)圖像。
8.PorterDuff.Mode.SRC_OUT
只在源圖像和目標(biāo)圖像不相交的地方繪制源圖像噪矛。
9.PorterDuff.Mode.DST_OUT
只在源圖像和目標(biāo)圖像不相交的地方繪制目標(biāo)圖像量蕊。
10.PorterDuff.Mode.SRC_ATOP
在源圖像和目標(biāo)圖像相交的地方繪制源圖像,在不相交的地方繪制目標(biāo)圖像艇挨。
11.PorterDuff.Mode.DST_ATOP
在源圖像和目標(biāo)圖像相交的地方繪制目標(biāo)圖像而在不相交的地方繪制源圖像残炮。
12.PorterDuff.Mode.XOR
異或:去除兩圖層交集部分
13.PorterDuff.Mode.DARKEN
取兩圖層全部區(qū)域,交集部分顏色加深
14.PorterDuff.Mode.LIGHTEN
取兩圖層全部缩滨,點亮交集部分顏色
15.PorterDuff.Mode.MULTIPLY
取兩圖層交集部分疊加后顏色
16.PorterDuff.Mode.SCREEN
濾色势就。
PorterDuff的源碼
package android.graphics;
這個類包含了alpha合成和混合模式的列表
可以傳遞給{@link PorterDuffXfermode},一個專門的實現(xiàn)
{@link Paint}的{@link Paint#setXfermode(Xfermode)傳輸模式}脉漏。
所有可用的模式都可以在{@link Mode}枚舉中找到
public class PorterDuff {
public enum Mode {
// these value must match their native equivalents. See SkXfermode.h
CLEAR (0),
SRC (1),
DST (2),
SRC_OVER (3),
DST_OVER (4),
SRC_IN (5),
DST_IN (6),
SRC_OUT (7),
DST_OUT (8),
SRC_ATOP (9),
DST_ATOP (10),
XOR (11),
DARKEN (16),
LIGHTEN (17),
MULTIPLY (13),
SCREEN (14),
ADD (12),
OVERLAY (15);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
/**
* @hide
*/
public final int nativeInt;
}
/**
* @hide
*/
public static int modeToInt(Mode mode) {
return mode.nativeInt;
}
/**
* @hide
*/
public static Mode intToMode(int val) {
switch (val) {
default:
case 0: return Mode.CLEAR;
case 1: return Mode.SRC;
case 2: return Mode.DST;
case 3: return Mode.SRC_OVER;
case 4: return Mode.DST_OVER;
case 5: return Mode.SRC_IN;
case 6: return Mode.DST_IN;
case 7: return Mode.SRC_OUT;
case 8: return Mode.DST_OUT;
case 9: return Mode.SRC_ATOP;
case 10: return Mode.DST_ATOP;
case 11: return Mode.XOR;
case 16: return Mode.DARKEN;
case 17: return Mode.LIGHTEN;
case 13: return Mode.MULTIPLY;
case 14: return Mode.SCREEN;
case 12: return Mode.ADD;
case 15: return Mode.OVERLAY;
}
}
}
3. applyColorMod()
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
}
圖片drawable的寬和高
final int dwidth = mDrawableWidth;
final int dheight = mDrawableHeight;
imageview用于顯示區(qū)域的寬和高
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
final boolean fits = (dwidth < 0 || vwidth == dwidth)
&& (dheight < 0 || vheight == dheight);
ScaleType.FIT_XY 的縮放類型
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的區(qū)域顯示
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);
ScaleType.MATRIX 的縮放類型
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) {
ScaleType.CENTER 的縮放類型
// 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) {
ScaleType.CENTER_CROP 的縮放類型
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) {
ScaleType.CENTER_INSIDE 的縮放類型
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
圖片寬高小于view寬高時蛋勺,不縮放
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;
剩下的Fit_XY等Fit系列,都在這里
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
private static final Matrix.ScaleToFit[] sS2FArray = {
Matrix.ScaleToFit.FILL,
Matrix.ScaleToFit.START,
Matrix.ScaleToFit.CENTER,
Matrix.ScaleToFit.END
};
private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) {
// ScaleToFit enum to their corresponding Matrix.ScaleToFit values
return sS2FArray[st.nativeInt - 1];
}
/**
*將矩陣設(shè)置為比例并轉(zhuǎn)換將源矩形映射到的值
*目標(biāo)矩形鸠删,如果結(jié)果可以表示抱完,則返回true。
*
* @param src要映射的源矩形刃泡。
* @參數(shù)dst要映射到的目標(biāo)矩形巧娱。
* @參數(shù)stf ScaleToFit選項
* @如果矩陣可以用矩形映射表示,則返回true烘贴。
*/
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
if (dst == null || src == null) {
throw new NullPointerException();
}
return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
}