StateListDrawable源码详解
背景
在开发过程中我们的按钮有时候会有点击和抬起用到不同的背景效果,一般我们是用selector.xml来实现的,那么android是如何解析这个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去设置。
StateListDrawable源码分析
selector.xml会被解析成一个StateListDrawable对象,然后进行解析。
先看一下StateListDrawable类图。
![](https://img.haomeiwen.com/i6356977/2d08c370a5a5273f.png)
StateListDrawable继承DrawableContainer,里面有一个StateListState对象,而在StateListState里面有一个mStateSets[][] 的二维数组,StateListState有继承于DrawableContainer中DrawableContainerState类,里面有一个 Drawable[] mDrawables。
当解析第一个item的时候,把一维数组stateSet 存在mStateSets的第0个位置,把drawable对应的资源存在mDrawables的第0个位置,解析第2个item的时候,把stateSet和drawable 存放mStateSets,mDrawables的第1位置,如此循环,直到解析完成。
看一下其中的源代码:
StateListDrawable里有一个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) {
//调用DrawableContainerState 的addChild()
final int pos = addChild(drawable);
mStateSets[pos] = stateSet;
return pos;
}
StateListState 继承 DrawableContainerState 直接调用父类的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;
}
代码实现xml功能
分析了解析过程后,其中selector.xml里可以直接用代码实现,比如在自定义View的时候就可以用到。
StateListDrawable stateListDrawable = new StateListDrawable();
//注意该处的顺序,只要有一个状态与之相配,背景就会被换掉
//所以不要把大范围放在前面了,如果sd.addState(new[]{},normal)放在第一个的话,就没有什么效果了
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));
//没有任何状态时显示的图片,就设置空集合,默认状态
stateListDrawable.addState(new int[]{}, getDrawable(android.R.attr.state_enabled));
其中“-”表示对应的属性为false。
网友评论