view的state和drawable的state 源码分析


上边根据当前view的各种状态,按位与StateSet.VIEW_STATE_xxx合成了一个int的viewStateIndex,接着就通过StateSet.get(viewStateIndex)来获取state的数组,

public static int[] get(int mask) {
    if (mask >= VIEW_STATE_SETS.length) {
        throw new IllegalArgumentException("Invalid state set mask");
    }
    return VIEW_STATE_SETS[mask];
}

看到是从VIEW_STATE_SETS数组获取的,看下它是怎么创建的

static finaint[] VIEW_STATE_IDS = new int[] {
        R.attr.state_window_focused,    VIEW_STATE_WINDOW_FOCUSED,
        R.attr.state_selected,          VIEW_STATE_SELECTED,
        R.attr.state_focused,           VIEW_STATE_FOCUSED,
        R.attr.state_enabled,           VIEW_STATE_ENABLED,
        R.attr.state_pressed,           VIEW_STATE_PRESSED,
        R.attr.state_activated,         VIEW_STATE_ACTIVATED,
        R.attr.state_accelerated,       VIEW_STATE_ACCELERATED,
        R.attr.state_hovered,           VIEW_STATE_HOVERED,
        R.attr.state_drag_can_accept,   VIEW_STATE_DRAG_CAN_ACCEPT,
        R.attr.state_drag_hovered,      VIEW_STATE_DRAG_HOVERED
};

static {
// 首先根据在R.styleable.ViewDrawableStates中定义的各个state的顺序来生成一个有顺序的VIEW_STATE_IDS—orderedIds
    finaint[] orderedIds = new int[VIEW_STATE_IDS.length];
    for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
        finaint viewState = R.styleable.ViewDrawableStates[i];
        for (int j = 0; j < VIEW_STATE_IDS.length; j += 2) {
            if (VIEW_STATE_IDS[j] == viewState) {
                orderedIds[i * 2] = viewState;
                orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
            }
        }
    }

// 然后用VIEW_STATE_SETS数组根据orderedIds来存储不同state的组合的固定顺序。
    finaint NUM_BITS = VIEW_STATE_IDS.length / 2;
    VIEW_STATE_SETS = new int[1 << NUM_BITS][];
    for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
        finaint numBits = Integer.bitCount(i);
        finaint[] set = new int[numBits];
        int pos = 0;
        for (int j = 0; j < orderedIds.length; j += 2) {
            if ((i & orderedIds[j + 1]) != 0) {
                set[pos++] = orderedIds[j];
            }
        }
        VIEW_STATE_SETS[i] = set;
    }
}

view的子类的独有state

发现经常使用的几个state并不在包含在上边,比如checked,

这是由于view的10中状态是基础状态,会到StateSet中去查找,这是所有view共有的状态,

而view的子类有些是有特殊的状态,如Checkbox的checked状态,一般是Override上边一些方法,并把自己的state加入进去。

如Checkbox的onCreateDrawableState:

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    finaint[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) {
        mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    }
    return drawableState;
}

drawalbe的state

WILD_CARD

public static finaint[] WILD_CARD = new int[0];

这个就是什么都没有state都没有定义的那个item。

这个特殊的state会匹配所有的状态,所以需要把此item放在最后一个,否则每次state改变获取的item都是这个什么state都没有定义的。

drawalbe.setState源码分析

接着上边的获取state后会把state设置进drawable中,以StateListDrawable为例:

public boolean setState(finaint[] stateSet) {
    if (!Arrays.equals(mStateSet, stateSet)) {
        mStateSet = stateSet;
        return onStateChange(stateSet);
    }
    return false;
}
@Override
protected boolean onStateChange(int[] stateSet) {
    finaboolean changed = super.onStateChange(stateSet);

    int idx = mStateListState.indexOfStateSet(stateSet);
    if (idx < 0) {
        idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
    }

    return selectDrawable(idx) || changed;
}

如果第一次没有找到的话就会,重新用StateSet.WILD_CARD进行查找。

StateListState.indexOfStateSet

int indexOfStateSet(int[] stateSet) {
    finaint[][] stateSets = mStateSets;
    finaint N = getChildCount();
    for (int i = 0; i < N; i++) {
        if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
            return i;
        }
    }
    return -1;
}

这里表有两个变量:

  • stateSets(二维数组),是通过addState加入的各种 state数组 而组成的二维数组,也就是说stateSets的第二维存储的是state数组,stateSets中的顺序是加入的顺序;
  • stateSet是根据view的状态组合成的一个有固定顺序的state数组,传递进来是要到stateSets中查询。

这个方法就是把stateSets中的所有state数组按加入的顺序 和 stateSet进行比较。

StateSet.stateSetMatches

public static boolean stateSetMatches(int[] stateSpec, int[] stateSet) {
    if (stateSet == null) {
        return (stateSpec == null || isWildCard(stateSpec));
    }
    int stateSpecSize = stateSpec.length;
    int stateSetSize = stateSet.length;
    for (int i = 0; i < stateSpecSize; i++) {
        int stateSpecState = stateSpec[i];
        if (stateSpecState == 0) {
            // We've reached the end of the cases to match against.
            return true;
        }

// 我们有时会定义 负的state来表示不能有此state,下边的代码就是处理这个逻辑的
        final boolean mustMatch;
        if (stateSpecState > 0) { // 正数表示必须要有
            mustMatch = true;
        } else { // 负数表示必须不能有
            // We use negative values to indicate must-NOT-match states.
            mustMatch = false;
            stateSpecState = -stateSpecState;
        }

        boolean found = false;
        for (int j = 0; j < stateSetSize; j++) {
            final int state = stateSet[j];
            if (state == 0) {
                // We've reached the end of states to match.
                if (mustMatch) {
                    // We didn't find this must-match state.
                    return false;
                } else {
                    // Continue checking other must-not-match states.
                    break;
                }
            }
            if (state == stateSpecState) {
                if (mustMatch) {
                    found = true;
                    // Continue checking other other must-match states.
                    break;
                } else {
                    // Any match of a must-not-match state returns false.
                    return false;
                }
            }
        } //for stateSetSize end

        if (mustMatch && !found) {
            // We've reached the end of states to match and we didn't
            // find a must-match state.
            return false;
        }
    } //for stateSpecSize end
    return true;
}

上边的代码的两个数组:

  • stateSet是根据当前view的各种状态计算出来的一个有顺序的state数组,这个数组的中的顺序是系统定义的。
  • stateSpec是我们addState时加入的state数组,这个数组的中的顺序是我们定义的。

这个方法里就是拿stateSpec中的每一个和stateSet中的每一个进行比较,

  • 如果stateSpec是WILD_CARD,也就是int[0],那么直接会到最后return true。
  • 如果stateSpec中有正数的state,那么就表示stateSet中必须要出现这个,
  • 如果stateSpec中有负数state,那么就表示stateSet中不能出现这个,

所以想要找到正确状态的drawable,就要在StateListDrawalbe.addState()的加入顺序上做调整。

例如:

StateListDrawalbe的加入的drawable顺序为:

  1. {selected}
  2. {enable}
  3. {selected , actived}

此时我们想要显示的是StateListDrawalbe最后一个对应的drawalbe,于是把把view设置为

view.setSelected(true);
view.setActivated(true);

根据上边view的state可知,最后view的当前状态数组是{enable , selected , actived },

但是如果根据上边stateSetMatches查找代码最终匹配的却是第一个{selected},

所以就需要对StateListDrawalbe.addState()的加入顺序上做调整,让{selected , actived}放到第一个即可。

selectDrawable

接着上边的,在找到对应的drawable序号后:

public boolean selectDrawable(int idx) {
    if (idx == mCurIndex) {
        return false;
    }

    final long now = SystemClock.uptimeMillis();
    if (mDrawableContainerState.mExitFadeDuration > 0) {
        if (mLastDrawable != null) {
            mLastDrawable.setVisible(false, false);
        }
        if (mCurrDrawable != null) {
            mLastDrawable = mCurrDrawable;
            mLastIndex = mCurIndex;
            mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;
        } else {
            mLastDrawable = null;
            mLastIndex = -1;
            mExitAnimationEnd = 0;
        }
    } else if (mCurrDrawable != null) {
        mCurrDrawable.setVisible(false, false);
    }

    if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
        final Drawable d = mDrawableContainerState.getChild(idx);
        mCurrDrawable = d;
        mCurIndex = idx;
        if (d != null) {
            if (mDrawableContainerState.mEnterFadeDuration > 0) {
                mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
            }
            initializeDrawableForDisplay(d);
        }
    } else {
        mCurrDrawable = null;
        mCurIndex = -1;
    }

    if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {
        if (mAnimationRunnable == null) {
            mAnimationRunnable = new Runnable() {
                @Override public void run() {
                    animate(true);
                    invalidateSelf();
                }
            };
        } else {
            unscheduleSelf(mAnimationRunnable);
        }
        // Compute first frame and schedule next animation.
        animate(true);
    }

    invalidateSelf();

    return true;
}