Android selector选择器自定义属性

Android selector选择器自定义属性

Android系统为UI的背景制定了灵活的绘制方案,drawable方式。开发者可以通过各种各样的drawable样式来为自己的UI灵活展示各自的风格。在selector选择器中,系统提供了类似state_enables的属性,让组件在不同的状态下展示不同的UI效果。

在笔者研(shi)究(yong)timessquare项目时,发现selector的属性可以自定义,瞬间像发现新大陆一样,激动得不得了。下面笔者将从中学到的东西做一点记录,以加深印象。

识别你的自定义属性

自定义View

自定义View的目的是为了让View拥有超出于系统的附加属性。

自定义属性

首先了解你的需求,系统可以满足的尽量用系统的,然后才选择自定义。

例如:我需要为不同的银行卡设定不同的背景。

实现方式1:定义很多个不同的shape或者使用系统的不同state状态为不同的银行卡进行背景的定义。Perfect!但是不是我的菜。

实现方式2:直接使用自定义View的onDraw方法绘制不同的背景。Perfect!但是这也不是我的菜。

实现方式3:为selector选择器增加额外的state属性。Perfect!这正是我要说的!

废话了一大推,下面开始自定义属性。

<declare-styleable name="BankCardContainerLayout">
   <attr name="bank_card_type" format="enum">
       <enum name="red" value="1" />
       <enum name="green" value="2" />
       <enum name="blue" value="3" />
   </attr>
   <attr name="bank_card_type_red" format="boolean" />
   <attr name="bank_card_type_blue" format="boolean" />
   <attr name="bank_card_type_green" format="boolean" />
</declare-styleable>

上面的三个boolean类型的属性,都是用在selector中的,用法类似系统的state_enabled

使用自定义的属性为selector添砖加瓦

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <item app:bank_card_type_red="true">
        <shape>
            <gradient android:angle="180" android:endColor="#e85c65" android:startColor="#e9527b" />
            <corners android:radius="0dp" />
        </shape>
    </item>
    <item app:bank_card_type_blue="true">
        <shape>
            <gradient android:angle="180" android:endColor="#41a4e5" android:startColor="#6280e0" />
            <corners android:radius="0dp" />
        </shape>
    </item>
    <item app:bank_card_type_green="true">
        <shape>
            <gradient android:angle="180" android:endColor="#1ab093" android:startColor="#0ca3ac" />
            <corners android:radius="0dp" />
        </shape>
    </item>
</selector>

在编辑selector时,引入xmlns:app="http://schemas.android.com/apk/res-auto"命名空间,以使用自定义属性。

实现需求


public class BankCardContainerLayout extends RelativeLayout {

    final int ext_attr[] = {
            R.attr.bank_card_type
    };

    final int ext_attr_red[] = {
            R.attr.bank_card_type_red
    };

    final int ext_attr_blue[] = {
            R.attr.bank_card_type_blue
    };

    final int ext_attr_green[] = {
            R.attr.bank_card_type_green
    };

    public static final int RED = 1;
    public static final int GREEN = 2;
    public static final int BLUE = 3;

    private int bk_type = 1;

    public BankCardContainerLayout(Context context) {
        this(context, null);
    }

    public BankCardContainerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BankCardContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, ext_attr);
        if (array != null) {
            setType(array.getInt(R.styleable.BankCardContainerLayout_bank_card_type, 1));
            array.recycle();
        }
    }

    /**
     * @param type {@link #RED}  {@link #GREEN}  {@link #BLUE}
     */
    public void setType(int type) {
        this.bk_type = type;
        refreshDrawableState();
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        int drawableState[] = super.onCreateDrawableState(extraSpace + 1);
        switch (bk_type) {
            case RED:
                mergeDrawableStates(drawableState, ext_attr_red);
                break;
            case GREEN:
                mergeDrawableStates(drawableState, ext_attr_green);
                break;
            case BLUE:
                mergeDrawableStates(drawableState, ext_attr_blue);
                break;
        }
        return drawableState;
    }
}

解析

关键方法

onCreateDrawableState方法

Generate the new Drawable state for this view. This is called by the view system when the cached Drawable state is determined to be invalid. To retrieve the current state, you should use getDrawableState().

view的drawable状态失效时,通过这个方法为view创建新的drawable状态。

例如:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="true" drawable="@color/red"/>
   <item android:state_enabled="false" drawable="@color/green"/>
</selector>

将以上selector作为某个view的背景时,通过setEnabled方法,就是一种引起view的drawable状态失效。

mergeDrawableState方法

Merge your own state values in additionalState into the base state values baseState that were returned by onCreateDrawableState(int)

将自定义state作为additionalStateonCreateDrawableState方法所返回的baseState进行合并。

refreshDrawableState方法

Call this to force a view to update its drawable state. This will cause drawableStateChanged to be called on this view. Views that are interested in the new state should call getDrawableState.

调用此方法强制刷新view的drawable状态。并且调用drawableStateChanged方法。控件一般通过getDrawableState方法来获取新的状态。

通过查看View的源码知晓,setEnabledsetSelected等一系列方法都会引起refreshDrawableState方法。

getDrawableState方法

Return an array of resource IDs of the drawable states representing the current state of the view.

调用流程

通过上述关键方法的描述可以大概了解自定义selector属性生效过程。这里用上一个例子加以描述。

  1. 自定义的setType方法实现了类似setEnabled方法的功能。当调动改方法时,通知自定义view更新drawable状态,即调用refreshDrawableState方法。
  2. refreshDrawableState方法内部调用drawableStateChanged方法。
  3. drawableStateChanged方法通过getDrawableState方法获取最新的drawable状态值。
  4. getDrawableState方法调用自定义类内的onCreateDrawableState方法来实现drawable状态的变更(通过mergeDrawableStates方法)。
  5. 通过新的drawable状态来更新view的背景。

最后

我的一枚程序员,但是英语真的很重要。没有什么是阅读源码解决不了的。

2017-03-14于被窝内

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容