Android selector選擇器自定義屬性
Android系統(tǒng)為UI的背景制定了靈活的繪制方案玻佩,drawable方式淆党。開發(fā)者可以通過各種各樣的drawable樣式來為自己的UI靈活展示各自的風(fēng)格。在selector選擇器中贮缕,系統(tǒng)提供了類似state_enables的屬性,讓組件在不同的狀態(tài)下展示不同的UI效果俺榆。
在筆者研(shi)究(yong)timessquare項(xiàng)目時(shí)感昼,發(fā)現(xiàn)selector的屬性可以自定義,瞬間像發(fā)現(xiàn)新大陸一樣罐脊,激動(dòng)得不得了定嗓。下面筆者將從中學(xué)到的東西做一點(diǎn)記錄蜕琴,以加深印象。
識別你的自定義屬性
自定義View
自定義View的目的是為了讓View擁有超出于系統(tǒng)的附加屬性宵溅。
自定義屬性
首先了解你的需求凌简,系統(tǒng)可以滿足的盡量用系統(tǒng)的,然后才選擇自定義恃逻。
例如:我需要為不同的銀行卡設(shè)定不同的背景雏搂。
實(shí)現(xiàn)方式1:定義很多個(gè)不同的shape或者使用系統(tǒng)的不同state狀態(tài)為不同的銀行卡進(jìn)行背景的定義。Perfect寇损!但是不是我的菜畔派。
實(shí)現(xiàn)方式2:直接使用自定義View的onDraw方法繪制不同的背景。Perfect润绵!但是這也不是我的菜线椰。
實(shí)現(xiàn)方式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>
上面的三個(gè)boolean類型的屬性配紫,都是用在selector中的,用法類似系統(tǒng)的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時(shí)躺孝,引入xmlns:app="http://schemas.android.com/apk/res-auto"
命名空間,以使用自定義屬性底桂。
實(shí)現(xiàn)需求
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;
}
}
解析
關(guān)鍵方法
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狀態(tài)失效時(shí)植袍,通過這個(gè)方法為view創(chuàng)建新的drawable狀態(tài)。
例如:
<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作為某個(gè)view的背景時(shí)籽懦,通過setEnabled方法于个,就是一種引起view的drawable狀態(tài)失效。
mergeDrawableState方法
Merge your own state values in additionalState into the base state values baseState that were returned by onCreateDrawableState(int)
將自定義state作為additionalState與onCreateDrawableState
方法所返回的baseState進(jìn)行合并暮顺。
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.
調(diào)用此方法強(qiáng)制刷新view的drawable狀態(tài)厅篓。并且調(diào)用drawableStateChanged
方法〈仿耄控件一般通過getDrawableState
方法來獲取新的狀態(tài)羽氮。
通過查看View
的源碼知曉,setEnabled
惫恼、setSelected
等一系列方法都會引起refreshDrawableState方法档押。
getDrawableState方法
Return an array of resource IDs of the drawable states representing the current state of the view.
調(diào)用流程
通過上述關(guān)鍵方法的描述可以大概了解自定義selector屬性生效過程。這里用上一個(gè)例子加以描述。
- 自定義的setType方法實(shí)現(xiàn)了類似setEnabled方法的功能汇荐。當(dāng)調(diào)動(dòng)改方法時(shí)洞就,通知自定義view更新drawable狀態(tài),即調(diào)用refreshDrawableState方法掀淘。
- refreshDrawableState方法內(nèi)部調(diào)用
drawableStateChanged
方法旬蟋。 - drawableStateChanged方法通過
getDrawableState
方法獲取最新的drawable狀態(tài)值。 - getDrawableState方法調(diào)用自定義類內(nèi)的onCreateDrawableState方法來實(shí)現(xiàn)drawable狀態(tài)的變更(通過mergeDrawableStates方法)革娄。
- 通過新的drawable狀態(tài)來更新view的背景倾贰。
最后
我的一枚程序員,但是英語真的很重要拦惋。沒有什么是閱讀源碼解決不了的匆浙。
2017-03-14于被窩內(nèi)