Android 中 View 的中的 DrawableState
来源:互联网 发布:mac怎么用ps 编辑:程序博客网 时间:2024/06/07 10:54
Android 中 View 的中的 DrawableState
挺久之前就会用 selector
这种资源来给一个 drawable
对象标识不同的状态了,但是之前的认识只停留在设置一个正常状态的图片,设置一个 state_pressed
状态的图片,然后把这个 selector
作为控件的背景,当控件被点击(前提是设置了可点击或者点击监听器)的时候图片会自动切换。但是 View
内部究竟是如何对 Drawable
对象的状态进行控制的,之前一直不清楚,直到前几天,因为工作的原因研究了一下相关的源码,终于对它有了一个比较深的认识。同时也学会如何 通过简单操作去改变控件中的 Drawable
的状态,相对于之前每次状态改变手动更换图片来说,控制 drawable state
的方式实在太爽了。
View
是如何控制 Drawable
状态的。
首先看和
Drawable
状态相关的几个方法:方法:
protected void drawableStateChanged()
javadoc: 这个方法会在View
的状态改变并影响到正在显示的drawable
的状态的时候会被调用。如果这个View
拥有一个StateListAnimator
的话,这个StateListAnimator
也会被调用来执行必要的状态改变动画。如果重写这个方法的话要确保调用父类的方法。
View 中的实现: 调用getDrawableState()
获得当前的drawable state
,并把它赋值给mBackground
和mStateListAnimator
。
ViewGroup 中的实现: 如果有FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE
标志的话,还会遍历每个子View
,如果子View
有DUPLICATE_PARENT_STATE
标志,就调用子View
的refreshDrawableState()
方法。方法: protected int[] onCreateDrawableState(int extraSpace)
javadoc注释: 为View
生成新的Drawable
的状态。这个方法会在缓存的Drawable state
被认为失效(invalid)之后被view
系统所调用。如果要获取当前的状态,你应该使用getDrawableState
方法。方法参数extraSpace
如果不是0的话,这个值可以代表你希望返回的数组中除了装载当前的状态之外,额外的空间,你可以使用这些空间来存放你自己的状态。
View 中的实现: 如果View
被设置为和父View
的drawable state
一致,返回父view
的drawable state
。否则从各种Flag
中获取view
的状态信息并封装到一个数组中返回(数组长度是收集到的信息数量加上参数extraSpace
的大小)。
ViewGroup 中的实现: 如果没有FLAG_ADD_STATES_FROM_CHILDREN
标志的话直接调用父方法返回。否则除了调用父方法,还要遍历子View
,通过getDrawableState
拿到它们的drawable state
,使用mergeDrawableStates
合并到自己的drawable state
。方法:
public final int[] getDrawableState()
javadoc: 返回一个资源 id 的数组,这些 id 代表着 View 的当前状态。
View中的实现: 如果没有 PFLAG_DRAWABLE_STATE_DIRTY 标志,直接返回缓存的 mDrawableState;否则,调用 onCreateDrawableState 获取新的状态返回,并把 PFLAG_DRAWABLE_STATE_DIRTY 标志去掉。
ViewGroup中的实现: 没有重载。方法:
protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState)
javadoc: 将你存储在 additionalState 中的状态和 baseState 中的状态合并到一起(baseState 通常是由 getDrawableState 方法得到的),为了简化,baseState 会作为参数被返回,也就是,baseArray 中必须提前预留存放 additionalState 的空间,否则也无法合并成功。
View中的实现: 从 baseState 数组第一个为 0 的元素开始,将 additionalState 数组中的内容拷贝过来,最后把 baseState 返回。
ViewGroup中的实现: 没有重载。方法:
public void refreshDrawableState()
javadoc: 这个方法被调用来强制更新一个 View 的 drawable state。这个方法会导致 View 的 drawableStateChanged 方法被调用。如果对新的 drawable state 感兴趣,可以通过 getDrawableState 方法获取。
View中的实现: 设置上 PFLAG_DRAWABLE_STATE_DIRTY 标志,调用 drawableStateChanged(),如果有父 View,调用父 View 的 childDrawableStateChanged() 方法。
ViewGroup中的实现: 没有重载。方法:
public void childDrawableStateChanged(View child)
javadoc: 这个是 ViewGroup 的方法。如果有 FLAG_ADD_STATES_FROM_CHILDREN 标志的话,刷新这个 ViewGroup 的 drawable state,将子 View 的状态加入进去.
View中的实现: 没有定义。
ViewGroup中的实现: 如果有FLAG_ADD_STATES_FROM_CHILDREN 标志,调用 refreshDrawableState() 方法。
然后我们来梳理一下这些方法调用的流程:
- 当
View
的状态(onCreateDrawableState
方法需要收集的各种标志)发生变化的时候,会调用refreshDrawableState
方法。 - 在
refreshDrawableState
方法中会设置上PFLAG_DRAWABLE_STATE_DIRTY
标志,然后调用drawableStateChanged
方法。 - 在
drawableStateChanged
方法中,会调用getDrawableState
方法获取当前状态,并把状态赋值给mBackground
和StateListAnimator
。 - 在
getDrawableState
方法中,会发现存在PFLAG_DRAWABLE_STATE_DIRTY
标志,缓存的drawable state
已经失效,就会依次查看所有与drawable state
相关的标志,组装新的drawable state
返回。
- 当
如何让自定义的控件中的 drawable
可以响应状态的变化
你在编程中可能会遇到这样的问题,当你自定义一个控件,而这个控件中除了有背景还可能会有其他的图案,你希望当你按在控件上的时候,所有的图案都发生状态的变化,但结果是只有背景发生了状态的改变。其他图案没有响应状态的变化,为什么呢?因为这些 drawable
没有被设置相应的状态。
想要让自己的 drawable
也像 mBackground
一样随着控件的状态变化而变化,就需要把上面那些方法中用到 mBackground
的位置也加上我们自己的 drawable
的操作,也就是 drawableStateChanged()
方法:
@Overrideprotected void drawableStateChanged() { super.drawableStateChanged(); // mDrawalbe 是我们自己的 drawable 对象,如果有更多,需要每个都进行这样的操作 Drawable d = mDrawable; if (d != null && d.isStateful()) { d.setState(getDrawableState()); }}
实际上
ImageView
中的drawableStateChanged
方法的实现和上面是一模一样的。
如何定义自己的 drawable state
首先,drawable state
是没办法自己定义的,所有的 drawable state
就是你在定义 selector
的时候可用的那些。这里说的定义自己的 drawable state
是指 在合适的时机使控件处于某个 drawable state
。比如,你能会希望自己的控件可以支持 state_check
, 在某种情况下所有的 drawable
都处于选中状态,另一种情况都处于未选中状态,就像 CheckBox
那样。
你需要这样做:
定义一个成员变量来标识当前的选中状态
private boolean mChecked;
定义一个改变选中状态的方法,在状态改变时调用
refreshDrawableState
方法public void setChecked(boolean checked) {if (mChecked != checked) { mChecked = checked; // 刷新 drawable state refreshDrawableState();}}
重写
onCreateDrawableState
, 根据自己的标志来确定是否加上更多状态// 代表选中状态的集合private static final int[] CHECK_STATE_SET = new int[] {android.R.attr.state_checked};@Overridepublic int[] onCreateDrawableState(int extraSpace) {if (!mChecked) { // 如果未选中,直接返回父类的结果 return super.onCreateDrawableState(extraSpace);} else { // 如果选中,将父类的结果和选中状态合并之后返回 return mergeDrawableStates( super.onCreateDrawableState(extraSpace + 1), CHECK_STATE_SET);}}
然后当你希望切换选中状态的时候,调用 setChecked
方法就行了。
- Android 中 View 的中的 DrawableState
- View的DrawableState(即StateListDrawable)变化的源码分析
- Android中view中的requestLayout和invalidate方法的区别
- Android中xml中的View标签小写的问题
- android中的View的对象
- Android 中的view 的渲染
- android中查找某个Activity中的view
- android中获得某个activity中的view
- android 中自定义View中的参数
- android 中View的平移
- Android中View的绘制
- Android中View的绘制
- Android中View的生命周期
- Android中View的遍历
- Android中View的绘制
- android中view的生命周期
- Android中View的滑动
- Android中View的绘制
- [Perl]找出目录下面以*.zip结尾的文件
- android 实践- 2015/07/06
- 基数排序
- CF 552-C. Vanya and Scales
- [转]PHP hook钩子类
- Android 中 View 的中的 DrawableState
- mysql悲观锁总结和实践for update
- 常用特殊符号的HTML代码(HTML字符实体)
- 使用HTTP请求协议之Post与Get方法的区别
- TA们是这样描述"睿哥"的
- Python图像处理(15):SVM分类器
- 返回值为对象调用拷贝构造函数
- 经典排序算法
- LeetCode216:Combination Sum III