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顺序为:
- {selected}
- {enable}
- {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; }