Android Drawable介紹
Android系統中將可繪製對象被抽象爲Drawable,不同的繪製資源對應着不同的Drawable類型。Android FrameWork提供了常用的Drawable,Android控件的繪製資源基本都是通過Drawable形式實現的。一般情況下,開發者是不會直接接觸Drawable的具體實現的,Drawable資源一般都放在res/drawable目錄下,用戶通過圖片,xml格式的Drawable資源來使用。
Android內置的比較常用的Drawable類型包括:ColorDrawable、GradientDrawable、ShapeDrawable、BitmapDrawable、 NinePatchDrawable、InsetDrawable、ClipDrawable、ScaleDrawable、RotateDrawable、AnimationDrawable、LayerDrawable、LevelListDrawable、StateListDrawable、TransitionDrawable。
一般情況下,除了圖片資源是直接放在res/drawable下(android studio中圖片放在res/minmap下),其他的Drawable都是以xml格式實現的,開發者通過在xml中使用shape,selector,level-list等標籤來實現對應的Drawable,從而實現相應的可繪製資源的定義,最終view通過繪製這些Drawable來實現我們想要的顯示效果。
Drawable中xml標籤與Drawable對象的對應關係如以下表格所示:
xml標籤 | Drawable對象 |
---|---|
<selector> | StateListDrawable |
<level-list> | LevelListDrawable |
<layer-list> | LayerDrawable |
<transition> | TransitionDrawable |
<color> | ColorDrawable |
<shape> | GradientDrawable |
<scale> | ScaleDrawable |
<scale> | ScaleDrawable |
<clip> | ClipDrawable |
<rotate> | RotateDrawable |
<anination-list> | AnimationDrawable |
<inset> | InsetDrawable |
<bitmap> | BitmapDrawable |
<nine-patch> | NinePatchDrawable |
下面我們通過Framework的源代碼來探索一下Drawable的加載過程,一般情況下,我們通過getResources().getDrawable(int id);的方式去加載。順着這條線往下看,getDrawable->loadDrawable->loadDrawableForCookie->Drawable.createFromXml->createFromXmlInner,來看android\graphics\drawable\Drawable.java的createFromXmlInner函數的實現代碼。
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final Drawable drawable;
final String name = parser.getName();
if (name.equals("selector")) {
drawable = new StateListDrawable();
} else if (name.equals("animated-selector")) {
drawable = new AnimatedStateListDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
drawable = new TransitionDrawable();
} else if (name.equals("ripple")) {
drawable = new RippleDrawable();
} else if (name.equals("color")) {
drawable = new ColorDrawable();
} else if (name.equals("shape")) {
drawable = new GradientDrawable();
} else if (name.equals("vector")) {
drawable = new VectorDrawable();
} else if (name.equals("animated-vector")) {
drawable = new AnimatedVectorDrawable();
} else if (name.equals("scale")) {
drawable = new ScaleDrawable();
} else if (name.equals("clip")) {
drawable = new ClipDrawable();
} else if (name.equals("rotate")) {
drawable = new RotateDrawable();
} else if (name.equals("animated-rotate")) {
drawable = new AnimatedRotateDrawable();
} else if (name.equals("animation-list")) {
drawable = new AnimationDrawable();
} else if (name.equals("inset")) {
drawable = new InsetDrawable();
} else if (name.equals("bitmap")) {
//noinspection deprecation
drawable = new BitmapDrawable(r);
if (r != null) {
((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else if (name.equals("nine-patch")) {
drawable = new NinePatchDrawable();
if (r != null) {
((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else {
throw new XmlPullParserException(parser.getPositionDescription() +
": invalid drawable tag " + name);
}
drawable.inflate(r, parser, attrs, theme);
return drawable;
}
上面的對應關係就從這裏來的,現在是不是一下子明朗了呢。在函數結尾,Drawable通過條用inflate函數解析AttributeSet中的屬性信息,並設置Drawable相應的屬性。看一下BitmapDrawable吧,
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
updateStateFromTypedArray(a);
verifyState(a);
a.recycle();
}
繼續看updateStateFromTypedArray
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
final Resources r = a.getResources();
final BitmapState state = mBitmapState;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
if (srcResId != 0) {
final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
if (bitmap == null) {
throw new XmlPullParserException(a.getPositionDescription() +
": <bitmap> requires a valid src attribute");
}
state.mBitmap = bitmap;
}
state.mTargetDensity = r.getDisplayMetrics().densityDpi;
final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false;
setMipMap(a.getBoolean(R.styleable.BitmapDrawable_mipMap, defMipMap));
state.mAutoMirrored = a.getBoolean(
R.styleable.BitmapDrawable_autoMirrored, state.mAutoMirrored);
state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, state.mBaseAlpha);
final int tintMode = a.getInt(R.styleable.BitmapDrawable_tintMode, -1);
if (tintMode != -1) {
state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
}
final ColorStateList tint = a.getColorStateList(R.styleable.BitmapDrawable_tint);
if (tint != null) {
state.mTint = tint;
}
final Paint paint = mBitmapState.mPaint;
paint.setAntiAlias(a.getBoolean(
R.styleable.BitmapDrawable_antialias, paint.isAntiAlias()));
paint.setFilterBitmap(a.getBoolean(
R.styleable.BitmapDrawable_filter, paint.isFilterBitmap()));
paint.setDither(a.getBoolean(R.styleable.BitmapDrawable_dither, paint.isDither()));
setGravity(a.getInt(R.styleable.BitmapDrawable_gravity, state.mGravity));
final int tileMode = a.getInt(R.styleable.BitmapDrawable_tileMode, TILE_MODE_UNDEFINED);
if (tileMode != TILE_MODE_UNDEFINED) {
final Shader.TileMode mode = parseTileMode(tileMode);
setTileModeXY(mode, mode);
}
final int tileModeX = a.getInt(R.styleable.BitmapDrawable_tileModeX, TILE_MODE_UNDEFINED);
if (tileModeX != TILE_MODE_UNDEFINED) {
setTileModeX(parseTileMode(tileModeX));
}
final int tileModeY = a.getInt(R.styleable.BitmapDrawable_tileModeY, TILE_MODE_UNDEFINED);
if (tileModeY != TILE_MODE_UNDEFINED) {
setTileModeY(parseTileMode(tileModeY));
}
// Update local properties.
initializeWithState(state, r);
}
updateStateFromTypedArray函數中,BitmapDrawable獲取到src屬性後,通過BitmapFactory加載圖像文件,並讀取用戶配置的屬性,設置BitmapDrawable的其他屬性。其他的Drawable的實現方式類似。