View的DrawableState(即StateListDrawable)变化的源码分析
来源:互联网 发布:淘宝店铺装修 编辑:程序博客网 时间:2024/06/07 13:03
XML文件中给Button控件设置android:background属性或者在代码里直接调用View.setBackgroundDrawable函数设置背景,这些恐怕每个android开发人员都干过。的确,为了让我们的应用表现的更加人性化,这些控件的状态变化是必不可少的。可是,对于为什么这样用就起作用,不知道大家分析过没有,最近我分析了一下,现在和大家共享一下。
一,从XML中解析出StateListDrawable的过程。见下图:
图1 从XML中解析出StateListDrawable的过程
从图1中,我们可以看到一个完整的从XML中解析出StateListDrawable的过程,觉得图已经画的比较清楚了,所以就不在多说了。
二,View的DrawableState的设置过程
图2 View的DrawableState的设置过程
图2有些地方感觉还要多说两句,
1,onCreateDrawableState函数里有完整当前视图控件状态判断的方法,具体代码如下:
int viewStateIndex = (((privateFlags & PRESSED) != 0) ? 1 : 0); // 下压状态判断。这个状态的设置一般是在setPressed函数里进行的。 viewStateIndex = (viewStateIndex << 1) + (((mViewFlags & ENABLED_MASK) == ENABLED) ? 1 : 0); // 使能状态判断。这个状态的设置一般是setEnabled函数里进行的。 viewStateIndex = (viewStateIndex << 1) + (isFocused() ? 1 : 0); // 焦点状态判断。这个状态的设置一般是requestFocus函数里进行的。 viewStateIndex = (viewStateIndex << 1) + (((privateFlags & SELECTED) != 0) ? 1 : 0); // 选择状态判断。这个状态的设置一般是setSelected函数里进行的。 final boolean hasWindowFocus = hasWindowFocus(); viewStateIndex = (viewStateIndex << 1) + (hasWindowFocus ? 1 : 0); // 视图所在窗口焦点状态判断。 drawableState = VIEW_STATE_SETS[viewStateIndex];
2,一个窗口中可以有多个视图处于selected状态,但是只能有一个处于focused状态;
3,视图处于focused状态时,不一定处于window_focused状态。典型的情况是,窗口初次创建时,ViewRoot会执行performTraversals(),执行中根视图会执行requestFocus()操作,此时一般会有一个视图处于focused状态;但是这个时候视图并不一定是处于window_focused状态,因为window_focused是由系统发出WINDOW_FOCUS_CHANGED消息时,让ViewRoot执行windowFocusChanged()来改变的。这些可以通过重写View的requestFocus()和onWindowFocusChanged()来证明;
4,数组View.VIEW_STATE_SETS是一个非常重要的成员变量,它记录View的所有可能状态。View.VIEW_STATE_SETS这个数组里的部分状态组合是用View.stateSetUnion函数计算出来的。考虑到这个数组成员在后面StateSet.stateSetMatches函数里将被使用来判断状态是否匹配,觉得有必要具体说一下,下面是它的实现代码:
private static int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) { final int stateSet1Length = stateSet1.length; final int stateSet2Length = stateSet2.length; final int[] newSet = new int[stateSet1Length + stateSet2Length]; // 新状态集合newSet大小是stateSet1和stateSet2大小之和,默认值都是0 int k = 0; int i = 0; int j = 0; // This is a merge of the two input state sets and assumes that the // input sets are sorted by the order imposed by ViewDrawableStates. for (int viewState : R.styleable.ViewDrawableStates) { if (i < stateSet1Length && stateSet1[i] == viewState) { newSet[k++] = viewState; // 把stateSet1里的状态值赋给newSet i++; } else if (j < stateSet2Length && stateSet2[j] == viewState) { newSet[k++] = viewState; // 把stateSet2里的状态值赋给newSet j++; } if (k > 1) { assert(newSet[k - 1] > newSet[k - 2]); } } return newSet; }
我们来看一下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" /> </declare-styleable>
从这里我们看到,其实数组R.styleable.ViewDrawableStates里其实就是R.attr.state_pressed,R.attr.state_focused,R.attr.state_selected ,R.attr.state_window_focused和 R.attr.state_enabled 的集合。用语言总结一下View.stateSetUnion函数的作用,将状态集合stateSet1和stateSet2里的状态值,按照状态值在数组R.styleable.ViewDrawableStates里的先后顺序赋给新状态集合newSet。
5,考虑到完整性和对称性,这里把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; } 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; } } } if (mustMatch && !found) { // We've reached the end of states to match and we didn't // find a must-match state. return false; } } return true; }
三,View的DrawableState变化过程。有了上面的基础,这里就非常简单了,我就举一个例子来具体说明一下吧,下图是View.setPressed的时序图:
图3 View.setPressed时序图
把图3和图2结合起来看,就可以清楚地理解状态变化是如何实现的了。
四,相关的一些小技巧
有时候UI可能不给开发人员背景图资源,但是还是希望开发人员做到Button下压时,Button上的文字颜色变暗。这个时候,就可以用这个小技巧来实现了:设置android:textColor属性。具体例子请参见《Android 中设置TextView的颜色setTextColor》
备注:
1,Button是TextView的子类。
2,相关代码实现在TextView.drawableStateChanged函数里
protected void drawableStateChanged() { super.drawableStateChanged(); // background在这里起作用了 if (mTextColor != null && mTextColor.isStateful() || (mHintTextColor != null && mHintTextColor.isStateful()) || (mLinkTextColor != null && mLinkTextColor.isStateful())) { updateTextColors(); // 就是这里textColor开始起作用了 } final Drawables dr = mDrawables; if (dr != null) { int[] state = getDrawableState(); if (dr.mDrawableTop != null && dr.mDrawableTop.isStateful()) { dr.mDrawableTop.setState(state); } if (dr.mDrawableBottom != null && dr.mDrawableBottom.isStateful()) { dr.mDrawableBottom.setState(state); } if (dr.mDrawableLeft != null && dr.mDrawableLeft.isStateful()) { dr.mDrawableLeft.setState(state); } if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) { dr.mDrawableRight.setState(state); } } }
- View的DrawableState(即StateListDrawable)变化的源码分析
- Android 中 View 的中的 DrawableState
- Android中View绘制各种状态的背景图片原理深入分析以及StateListDrawable使用
- StateListDrawable 按下不能自动变化的问题
- 动态更换view类的背景---StateListDrawable的应用
- View的dispatchKeyEvent源码分析
- view的frame变化
- StateListDrawable的使用
- StateListDrawable的应用
- StateListDrawable的使用
- StateListDrawable的使用
- button的StateListDrawable
- Android的StateListDrawable详解
- [Android源码分析]inquiry result引起的上层变化分析
- android源码分析之View的事件分发(上)
- View的事件分发机制(源码分析)
- xUtils3源码分析(一):view的绑定
- 源码跟踪分析View的事件分发(改)
- 约瑟夫问题
- 2012-10-19 11gR2 concepts page 327 - 454
- 高性能mysql
- Android中StatFs获取系统/sdcard存储(剩余空间)大小
- Struts2总结之Action和Result
- View的DrawableState(即StateListDrawable)变化的源码分析
- LSA and PLSA笔记
- C++第8周项目2-5参考解答
- Struts2总结之拦截器
- 分手快乐,祝你快乐,你找不到比我更好的。
- 2013年各大小IT公司待遇,绝对真实,一线数据!(转好网)
- Struts2总结之控制流程
- TC35发送中文短信调试笔记
- BURG 引导也疯狂