Android 中 View 的中的 DrawableState

原创 2015年07月06日 21:33:53

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,并把它赋值给 mBackgroundmStateListAnimator
      ViewGroup 中的实现: 如果有 FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE 标志的话,还会遍历每个子 View,如果子 ViewDUPLICATE_PARENT_STATE 标志,就调用子 ViewrefreshDrawableState() 方法。

    • 方法: protected int[] onCreateDrawableState(int extraSpace)
      javadoc注释:View 生成新的 Drawable 的状态。这个方法会在缓存的 Drawable state 被认为失效(invalid)之后被 view 系统所调用。如果要获取当前的状态,你应该使用 getDrawableState 方法。方法参数 extraSpace 如果不是0的话,这个值可以代表你希望返回的数组中除了装载当前的状态之外,额外的空间,你可以使用这些空间来存放你自己的状态。
      View 中的实现: 如果 View 被设置为和父 Viewdrawable state 一致,返回父 viewdrawable 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() 方法。

  • 然后我们来梳理一下这些方法调用的流程:

    1. View 的状态(onCreateDrawableState方法需要收集的各种标志)发生变化的时候,会调用 refreshDrawableState 方法。
    2. refreshDrawableState 方法中会设置上 PFLAG_DRAWABLE_STATE_DIRTY 标志,然后调用 drawableStateChanged 方法。
    3. drawableStateChanged 方法中,会调用 getDrawableState 方法获取当前状态,并把状态赋值给 mBackgroundStateListAnimator
    4. getDrawableState 方法中,会发现存在 PFLAG_DRAWABLE_STATE_DIRTY 标志,缓存的 drawable state 已经失效,就会依次查看所有与 drawable state 相关的标志,组装新的 drawable state 返回。

如何让自定义的控件中的 drawable 可以响应状态的变化

你在编程中可能会遇到这样的问题,当你自定义一个控件,而这个控件中除了有背景还可能会有其他的图案,你希望当你按在控件上的时候,所有的图案都发生状态的变化,但结果是只有背景发生了状态的改变。其他图案没有响应状态的变化,为什么呢?因为这些 drawable 没有被设置相应的状态。

想要让自己的 drawable 也像 mBackground 一样随着控件的状态变化而变化,就需要把上面那些方法中用到 mBackground 的位置也加上我们自己的 drawable 的操作,也就是 drawableStateChanged() 方法:

@Override
protected 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 那样。

你需要这样做:

  1. 定义一个成员变量来标识当前的选中状态

    private boolean mChecked;
  2. 定义一个改变选中状态的方法,在状态改变时调用 refreshDrawableState 方法

    public void setChecked(boolean checked) {
    if (mChecked != checked) {
        mChecked = checked;
        // 刷新 drawable state
        refreshDrawableState();
    }
    }
  3. 重写 onCreateDrawableState, 根据自己的标志来确定是否加上更多状态

    // 代表选中状态的集合
    private static final int[] CHECK_STATE_SET = new int[] {
    android.R.attr.state_checked
    };
    @Override
    public int[] onCreateDrawableState(int extraSpace) {
    if (!mChecked) {
        // 如果未选中,直接返回父类的结果
        return super.onCreateDrawableState(extraSpace);
    } else {
        // 如果选中,将父类的结果和选中状态合并之后返回
        return mergeDrawableStates(
                super.onCreateDrawableState(extraSpace + 1), CHECK_STATE_SET);
    }
    }

然后当你希望切换选中状态的时候,调用 setChecked 方法就行了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android动态设置纯色图标的颜色

以前做了一个流量悬浮窗,悬浮窗里有当前网络状态的图标和网速的文字,想实现改变文字颜色的同时改变状态图标的颜色,实现后分享出来让大家参考下

Android视图状态及重绘流程分析,带你一步步深入了解View(三)

在前面一篇文章中,我和大家一起从源码的层面上分析了视图的绘制流程,了解了视图绘制流程中onMeasure、onLayout、onDraw这三个最重要步骤的工作原理,那么今天我们将继续对View进行深入...

Android Drawable 那些不为人知的高效用法

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43752383,本文出自:【张鸿洋的博客】1、概述Drawable在我们平时的开发...

Android 修改Bitmap 图片像素的信息 R G B 颜色值 详解

修改Bitmap 图片像素的信息 R G B 颜色值      要想修改Bitmap图片的 R G B信息 首先 得先拿到这张图片每个点的Color值 然后根据这个Color值 就可以算出对应的R G...

android RGB与int型颜色互转

一、将RGB颜色转化为int int color=Color.rgb(red, green, blue) 二、将int颜色转化为RGB int color=-4253158; int red...

详解refreshDrawableList()的执行流程

在Checkable Views和Android中自定义drawable states文章中,我们可以很清楚的看到当我们自定义图片状态列表的时候,当View的状态发生改变的时候,例如clicked,c...

Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解

本文原创,转载请注明出处:http://blog.csdn.net/qinjuning       今天继续给大家分享下View的相关知识,重点有一下两点:          ...

View的DrawableState(即StateListDrawable)变化的源码分析

XML文件中给Button控件设置android:background属性或者在代码里直接调用View.setBackgroundDrawable函数设置背景,这些恐怕每个android开发人员都干过...

View的drawableStateChanged()方法

Android 的View源码中有

Android自定义View的实现方法,带你一步步深入了解View(四)

不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回顾一下,我们一共学习了LayoutInflater的原理分析、视图的绘制流程、视图的状态及重绘等知识,算是把View中很多重要的知识...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)