StateListDrawable源码详解

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类图。

stateListDrawable.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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,658评论 25 709
  • 有时候觉得好累,有一个怎么都不知道过日子的哥哥,总是让我觉得日子在慢慢好起来的时候硬生生把我拖回到他的现实中。和妈...
    小阿懵阅读 1,904评论 0 0
  • 不管怎样都不做让自己后悔的事 糟糕的日子过完了就都是好运 我的世界太平静了只有我一个人 太久了 久的忘了怎么去羡慕...
    广平王妃阅读 3,077评论 0 0
  • 妮宝,明天就是你的生日会,画画课一结束,我们就开启了愉快的购物之旅,提前开心了一天。买了好多小玩意,还吃了汉堡,一...
    恩企妈妈阅读 1,137评论 0 0