zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    zabbix 微信告警
    mongodb 的数据备份与还原
    Templates
    Rsyslog 基本结构
    matplotlib绘图学习
    pyhon 简介
    Pygame (Python编程从入门到实践)
    AttributeError: 'pygame.Surface' object has no attribute 'rect'
    PYTHON CLIENT API
    SALT.STATES.CMD模块
  • 原文地址:https://www.cnblogs.com/muouren/p/14455245.html
Copyright © 2011-2022 走看看