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作为additionalState与onCreateDrawableState
方法所返回的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
的源码知晓,setEnabled
、setSelected
等一系列方法都会引起refreshDrawableState方法。
getDrawableState方法
Return an array of resource IDs of the drawable states representing the current state of the view.
调用流程
通过上述关键方法的描述可以大概了解自定义selector属性生效过程。这里用上一个例子加以描述。
- 自定义的setType方法实现了类似setEnabled方法的功能。当调动改方法时,通知自定义view更新drawable状态,即调用refreshDrawableState方法。
- refreshDrawableState方法内部调用
drawableStateChanged
方法。 - drawableStateChanged方法通过
getDrawableState
方法获取最新的drawable状态值。 - getDrawableState方法调用自定义类内的onCreateDrawableState方法来实现drawable状态的变更(通过mergeDrawableStates方法)。
- 通过新的drawable状态来更新view的背景。
最后
我的一枚程序员,但是英语真的很重要。没有什么是阅读源码解决不了的。
2017-03-14于被窝内