view的state和drawable的state 源码分析

view的state源码分析

view的setPressed,setSelected等方法中都会更新state,进而更新显示的drawalbe的状态。

调用会改变state的方法后,都会调用到view.refreshDrawableState()

public void refreshDrawableState() {
    mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
    drawableStateChanged();

    ViewParent parent = mParent;
    if (parent != null) {
        parent.childDrawableStateChanged(this);
    }
}
protected void drawableStateChanged() {
    finaint[] state = getDrawableState();

    finaDrawable bg = mBackground;
    if (bg != nul&& bg.isStateful()) {
        bg.setState(state);
    }

    finaDrawable fg = mForegroundInfo != nul? mForegroundInfo.mDrawable : null;
    if (fg != nul&& fg.isStateful()) {
        fg.setState(state);
    }

    if (mScrollCache != null) {
        finaDrawable scrollBar = mScrollCache.scrollBar;
        if (scrollBar != nul&& scrollBar.isStateful()) {
            scrollBar.setState(state);
        }
    }

    if (mStateListAnimator != null) {
        mStateListAnimator.setState(state);
    }
}
public finaint[] getDrawableState() {
    if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) {
        return mDrawableState;
    } else {
        mDrawableState = onCreateDrawableState(0);
        mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY;
        return mDrawableState;
    }
}
protected int[] onCreateDrawableState(int extraSpace) {
    if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
            mParent instanceof View) {
        return ((View) mParent).onCreateDrawableState(extraSpace);
    }

    int[] drawableState;

    int privateFlags = mPrivateFlags;
    
// 根据当前状态来合成一个viewStateIndex 
    int viewStateIndex = 0;
    if ((privateFlags & PFLAG_PRESSED) != 0) viewStateIndex |= StateSet.VIEW_STATE_PRESSED;
    if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= StateSet.VIEW_STATE_ENABLED;
    if (isFocused()) viewStateIndex |= StateSet.VIEW_STATE_FOCUSED;
    if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
    if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
    if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
    if (mAttachInfo != nul&& mAttachInfo.mHardwareAccelerationRequested &&
            HardwareRenderer.isAvailable()) {
        viewStateIndex |= StateSet.VIEW_STATE_ACCELERATED;
    }
    if ((privateFlags & PFLAG_HOVERED) != 0) viewStateIndex |= StateSet.VIEW_STATE_HOVERED;

    finaint privateFlags2 = mPrivateFlags2;
    if ((privateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0) {
        viewStateIndex |= StateSet.VIEW_STATE_DRAG_CAN_ACCEPT;
    }
    if ((privateFlags2 & PFLAG2_DRAG_HOVERED) != 0) {
        viewStateIndex |= StateSet.VIEW_STATE_DRAG_HOVERED;
    }
   
// 用合成的viewStateIndex获取一个有顺序的drawableState 数组。
    drawableState = StateSet.get(viewStateIndex);

    if (extraSpace == 0) {
        return drawableState;
    }

    finaint[] fullState;
    if (drawableState != null) {
        fullState = new int[drawableState.length + extraSpace];
        System.arraycopy(drawableState, 0, fullState, 0, drawableState.length);
    } else {
        fullState = new int[extraSpace];
    }

    return fullState;
}

view的通用state

可以看到普通view支持的state只有10个,都是根据当前view的各种状态来组合一个viewStateIndex,然后就会去android.util.StateSet中去查找对应的state数组。

viewStateIndex按位算的,有10种状态占10位,某一位是1表示有此状态,0表示无此状态:

public static finaint VIEW_STATE_WINDOW_FOCUSED = 1;
public static finaint VIEW_STATE_SELECTED = 1 << 1;
public static finaint VIEW_STATE_FOCUSED = 1 << 2;
public static finaint VIEW_STATE_ENABLED = 1 << 3;
public static finaint VIEW_STATE_PRESSED = 1 << 4;
public static finaint VIEW_STATE_ACTIVATED = 1 << 5;
public static finaint VIEW_STATE_ACCELERATED = 1 << 6;
public static finaint VIEW_STATE_HOVERED = 1 << 7;
public static finaint VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
public static finaint VIEW_STATE_DRAG_HOVERED = 1 << 9;

state的系统顺序

由于存在多种状态可以组合使用,即使相同的一组state的组合顺序也会不一样,那么就存在太多情况了,怎么办呢,Android官方的做法是用一个固定的state组合顺序来表示相同的一组state的所有不同顺序的组合。

这个顺序是在系统的attr.xml中的R.styleable.ViewDrawableStates定义的。

<declare-styleable name="ViewDrawableStates">
    <attr name="state_pressed" />
    <attr name="state_focused" />
    <attr name="state_selected" />
    <attr name="state_window_focused" />
    <attr name="state_enabled" />
    <attr name="state_activated" />
    <attr name="state_accelerated" />
    <attr name="state_hovered" />
    <attr name="state_drag_can_accept" />
    <attr name="state_drag_hovered" />
</declare-styleable>

比如当前view状态是enabled,selected,不管状态按什么顺序组合,那么对应的状态集都是{android.R.attr.state_selected, android.R.attr.state_enabled}。

源码

上边根据当前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;
}
原文地址:https://www.cnblogs.com/muouren/p/14455245.html