天天看點

如何自定義`Selector`狀态選擇器

為撒要使用

Selector

狀态選擇器?!

Selector

狀态選擇器的使用肯定是超級多的。有時候自定義某個控件,也需要對BackGround等作統一的狀态修改,這時候要是可以響應狀态變化使用到狀态選擇器而不是自己去手動的setBackGround那才是極好的啊!!

其實一開始我也不知道這個是怎麼去觸發相關的狀态更改的。但是這個肯定是定義在View裡面的,然後,肯定是有一個觸發事件,(一般就是點選事件啦),然後,肯定是需要通知View去重新繪制的。

Android中就能找到的相關代碼!

@Override
public int[] onCreateDrawableState(int extraSpace) {
   return super.onCreateDrawableState(extraSpace);
}
           
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

.

找了找,在TextView中,有如下的定義:

這是在View中

setEnabled

的方法。

@RemotableViewMethod
public void setEnabled(boolean enabled) {
    if (enabled == isEnabled()) return;

    setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);

    /*
     * The View most likely has to change its appearance, so refresh
     * the drawable state.
     */
    refreshDrawableState();

    // Invalidate too, since the default behavior for views is to be
    // be drawn at 50% alpha rather than to change the drawable.
    invalidate(true);

    if (!enabled) {
        cancelPendingInputEvents();
    }
}
           

在TextView中的

onCreateDrawableState

的方法。

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState;

    if (mSingleLine) {
        drawableState = super.onCreateDrawableState(extraSpace);
    } else {
        drawableState = super.onCreateDrawableState(extraSpace + 1);
        mergeDrawableStates(drawableState, MULTILINE_STATE_SET);
    }
           

這裡這個

MULTILINE_STATE_SET

是醬紫滴。

// New state used to change background based on whether this TextView is multiline.
private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
           

在View中對應的

mergeDrawableStates

的方法。

protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
    final int N = baseState.length;
    int i = N - 1;
    while (i >= 0 && baseState[i] == 0) {
        i--;
    }
    System.arraycopy(additionalState, 0, baseState, i + 1, additionalState.length);
    return baseState;
}
           

看到這裡,其實就可以明白,我們要做的就是把我們定義的狀态給它預設添加進去,然後還要加入一個條件,就是狀态改變的觸發條件。

自己做一個!

  • 定義個

    STATE_FOCUSED

    狀态

private static final int[] STATE_FOCUSED = new int[]{android.R.attr.state_focused};

  • 實時添加這個狀态,複寫

    onCreateDrawableState

    的方法,根據情況決定是否添加。
    @Override
    public int[] onCreateDrawableState(int extraSpace) {
        int[] states = super.onCreateDrawableState(extraSpace + 1);
        if (pressed) {
            mergeDrawableStates(states, STATE_FOCUSED);
        }
        return states;
    }
               
  • 決定觸發條件。
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                refreshState(true);
                break;
    
            case MotionEvent.ACTION_UP:
                refreshState(false);
                break;
            default:
                break;
        }
        return true;
    }
    
    
    
    
    
    private void refreshState(boolean state) {
            if (pressed != state) {
                pressed = state;
                refreshDrawableState();
            }
        }
               

這裡就是那個press狀态的修改的地方。在

refreshState

的方法中進行狀态的修改,如果的确是修改了,那麼就調用

refreshDrawableState

的方法,這個方法一定要調用,不然不重畫,你設定的狀态壓根不起作用。

最後的效果是醬紫滴!!就是最右邊那個

IndexBar

啦!!! 相關源碼和Demo下載下傳請移步 —>我的GitHub<—

如何自定義`Selector`狀态選擇器