在android開發中我們常常需要對控件進行相關操作,雖然網上已有很多對控件酷炫的操作,但小編今天給大家分享的純屬實用出發。在查看了一些列安卓教程和文檔后,發現了一位大牛分享的非常不錯的有關android自定義控件增加狀態的文章,分享給大家,學習、參考。
場景
View類是Android UI組件的基礎構建模塊,主要負責組件的繪制及事件的處理。我們在一些自定義控件的場合,可能需要在一個組件上畫些東西,也是通過重寫View的onDraw方法,通過其參數的Canvas對象進行繪制。
我們學習<selector/>的時候,就知道了關於一個視圖組件會有許多種狀態,比如按下(pressed),選擇(selected),可用(enabled),正常狀態,其他狀態等等。View也處理了關於一個組件在不同狀態下的顯示的繪制邏輯,通常繼承自View的組件都有着以上所說的這些狀態。但是也有一些狀態是View沒有提供的,而我們可能正需要它們,所以就需要對狀態進行擴展,增加我們的狀態,比如增加checked。
這里有一個具體的場景:
這是一個開關按鈕,開關狀態下背景不同,文字不同,文字旁邊的圖片也不同。狀態我用checked,文字我定義了兩個屬性:onText以及offText,文字旁邊的圖片我打算只用一個foreground屬性,但需要寫一個selector來定義正常狀態(未鎖)和checked狀態(鎖定)下的圖片。
實現
首先寫一個類繼承自TextView,因為我打算用TextView的setCompoundDrawables來設定文字旁邊的圖片。
然后定義屬性:
<declare-styleable name="ToggleView">
<attr name="android:foreground"/>
<attr name="pwOnText"/>
<attr name="pwOffText"/>
<attr name="pwColor"/>
<attr name="pwDrawableHeight"/>
</declare-styleable>
讀取屬性:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleView);
stateListDrawable = (StateListDrawable) a.getDrawable(R.styleable.ToggleView_android_foreground);
colorStateList = a.getColorStateList(R.styleable.ToggleView_pwColor);
offText = a.getString(R.styleable.ToggleView_pwOffText);
onText = a.getString(R.styleable.ToggleView_pwOnText);
drawableHeight = a.getDimensionPixelSize(R.styleable.ToggleView_pwDrawableHeight, 0);
a.recycle();
通過在類中實現Checkable接口,可以完成選中的邏輯,但是畫出來的狀態卻沒有更新,所以接下來的實現過程就是本篇的主要內容:
首先定義狀態集:
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
然后我們要把狀態給加進去。我們需要重寫protected int[] onCreateDrawableState(int extraSpace)方法,如下:
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
先調用父類的onCreateDrawableState方法得到狀態數組對象drawableState,但是參數extraSpace要加上1,因為我們要往里面增加一個狀態。然后判斷在代碼邏輯中,是否為選中狀態,如果是的話,調用mergeDrawableStates(drawableState, CHECKED_STATE_SET)方法把我們的狀態值給加進去,最終返回drawableState。
但是我們雖然實現了Checkable接口,在設置狀態時卻沒有觸發到這個狀態。所以我們需要自己去觸發這個狀態。
@Override
public void setChecked(boolean checked) {
if (isChecked != checked) {
isChecked = checked;
refreshDrawableState();
}
}
在狀態改變時,調用refreshDrawableState()刷新狀態。
最后,我們要重寫drawableStateChanged()方法,獲取到當前狀態的drawable,然后繪制出來。
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (stateListDrawable != null) {
int[] myDrawableState = getDrawableState();
stateListDrawable.setState(myDrawableState);
Drawable drawable = stateListDrawable.getCurrent();
if(drawableHeight != 0) {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth() * drawableHeight / drawable.getIntrinsicHeight(), drawableHeight);
} else {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
if (isChecked) {
setCompoundDrawables(drawable, null, null, null);
} else {
setCompoundDrawables(null, null, drawable, null);
}
}
setText(isChecked ? onText : offText);
}
這部分代碼中我需要設定drawable的大小,以及在不同的狀態下設置drawable的位置,因此稍微比較復雜一點點,實際上邏輯只需要如下:
獲取當前的drawableState狀態
對stateListDrawable(帶狀態的drawable集)設置狀態。
獲取stateListDrawable的當前狀態的drawable
進行你所想要的繪制。
這樣就完成了。
總結
從上面可知,增加狀態的過程如下:
定義狀態數組
重寫protected int[] onCreateDrawableState(int extraSpace)
調用refreshDrawableState()
重寫protected void drawableStateChanged()
以上就是android自定義控件增加狀態的方法,如果你還有更好的實現方法,歡迎補充分享。
相關文章:《如何實現自己的Android MVP框架?》