StateListDrawable源碼詳解
背景
在開發(fā)過程中我們的按鈕有時(shí)候會(huì)有點(diǎn)擊和抬起用到不同的背景效果康二,一般我們是用selector.xml來實(shí)現(xiàn)的,那么android是如何解析這個(gè)xml文件的呢苍在?
xml形式
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/button_press"
android:state_enabled="true"
android:state_pressed="true" />
<item
android:drawable="@drawable/button_press"
android:state_enabled="false" />
<item
android:drawable="@drawable/button_press" />
</selector>
然后button通過 background去設(shè)置。
StateListDrawable源碼分析
selector.xml會(huì)被解析成一個(gè)StateListDrawable對象,然后進(jìn)行解析。
先看一下StateListDrawable類圖灭忠。
StateListDrawable繼承DrawableContainer,里面有一個(gè)StateListState對象座硕,而在StateListState里面有一個(gè)mStateSets[][] 的二維數(shù)組弛作,StateListState有繼承于DrawableContainer中DrawableContainerState類,里面有一個(gè) Drawable[] mDrawables华匾。
當(dāng)解析第一個(gè)item的時(shí)候映琳,把一維數(shù)組stateSet 存在mStateSets的第0個(gè)位置,把drawable對應(yīng)的資源存在mDrawables的第0個(gè)位置蜘拉,解析第2個(gè)item的時(shí)候萨西,把stateSet和drawable 存放mStateSets,mDrawables的第1位置旭旭,如此循環(huán)谎脯,直到解析完成。
看一下其中的源代碼:
StateListDrawable里有一個(gè)addState的方法:
/**
* Add a new image/string ID to the set of images.
*
* @param stateSet - An array of resource Ids to associate with the image.
* Switch to this image by calling setState().
* @param drawable -The image to show.
*/
public void addState(int[] stateSet, Drawable drawable) {
if (drawable != null) {
//將stateSet和drawable 保存起來
mStateListState.addStateSet(stateSet, drawable);
// in case the new state matches our current state...
onStateChange(getState());
}
}
StateListState中的addStateSet:
int addStateSet(int[] stateSet, Drawable drawable) {
//調(diào)用DrawableContainerState 的addChild()
final int pos = addChild(drawable);
mStateSets[pos] = stateSet;
return pos;
}
StateListState 繼承 DrawableContainerState 直接調(diào)用父類的addChild:
/**
* Adds the drawable to the end of the list of contained drawables.
*
* @param dr the drawable to add
* @return the position of the drawable within the container
*/
public final int addChild(Drawable dr) {
final int pos = mNumChildren;
if (pos >= mDrawables.length) {
growArray(pos, pos+10);
}
dr.mutate();
dr.setVisible(false, true);
dr.setCallback(mOwner);
mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
mCheckedStateful = false;
mCheckedOpacity = false;
mConstantPadding = null;
mCheckedPadding = false;
mCheckedConstantSize = false;
mCheckedConstantState = false;
return pos;
}
代碼實(shí)現(xiàn)xml功能
分析了解析過程后持寄,其中selector.xml里可以直接用代碼實(shí)現(xiàn)源梭,比如在自定義View的時(shí)候就可以用到。
StateListDrawable stateListDrawable = new StateListDrawable();
//注意該處的順序稍味,只要有一個(gè)狀態(tài)與之相配废麻,背景就會(huì)被換掉
//所以不要把大范圍放在前面了,如果sd.addState(new[]{},normal)放在第一個(gè)的話模庐,就沒有什么效果了
stateListDrawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, getDrawable(android.R.attr.state_pressed));
stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, getDrawable(-android.R.attr.state_enabled));
//沒有任何狀態(tài)時(shí)顯示的圖片烛愧,就設(shè)置空集合,默認(rèn)狀態(tài)
stateListDrawable.addState(new int[]{}, getDrawable(android.R.attr.state_enabled));
其中“-”表示對應(yīng)的屬性為false赖欣。