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

1、View的几种不同状态属性

2、如何根据不同状态去切换我们的背景图片。

开篇介绍:android背景选择器selector用法汇总

对Android开发有经验的同学,对 ****节点的使用一定很熟悉,该节点的作用就是定义一组状态资源图片,使其能够

在不同的状态下更换某个View的背景图片。例如,如下的hello_selection.xml文件定义:

[java]  view plain copy print ?

  1. <?xml version="1.0" encoding="utf-8" ?>     
  2. <selector xmlns:android=“http://schemas.android.com/apk/res/android”>

  3. <item android:state_pressed=“true” android:state_window_focused=“true” android:drawable= “@drawable/pic1” />

  4. <item android:state_pressed=“true” android:state_focused=“false” android:drawable=“@drawable/pic2” />

  5. <item android:state_selected=“true” android:drawable=“@drawable/pic3” />

  6. <item android:state_focused=“true” android:drawable=“@drawable/pic4” />

  7.   

更多关于 节点的使用请参考该博客<android背景选择器selector用法汇总>

其实,前面说的xml文件,最终会被Android框架解析成StateListDrawable类对象。

知识点一:StateListDrawable类介绍

类功能说明:该类定义了不同状态值下与之对应的图片资源,即我们可以利用该类保存多种状态值,多种图片资源。

常用方法为:

public void addState (int[] stateSet, Drawable drawable)

功能: 给特定的状态集合设置drawable图片资源

使用方式:参考前面的hello_selection.xml文件,我们利用代码去构建一个相同的StateListDrawable类对象,如下:

[java]  view plain copy print ?

  1. //初始化一个空对象

  2. StateListDrawable stalistDrawable = new StateListDrawable();

  3. //获取对应的属性值 Android框架自带的属性 attr

  4. int pressed = android.R.attr.state_pressed;

  5. int window_focused = android.R.attr.state_window_focused;

  6. int focused = android.R.attr.state_focused;

  7. int selected = android.R.attr.state_selected;

  8. stalistDrawable.addState(new int []{pressed , window_focused}, getResources().getDrawable(R.drawable.pic1));

  9. stalistDrawable.addState(new int []{pressed , -focused}, getResources().getDrawable(R.drawable.pic2);

  10. stalistDrawable.addState(new int []{selected }, getResources().getDrawable(R.drawable.pic3);

  11. stalistDrawable.addState(new int []{focused }, getResources().getDrawable(R.drawable.pic4);

  12. //没有任何状态时显示的图片,我们给它设置我空集合

  13. stalistDrawable.addState(new int []{}, getResources().getDrawable(R.drawable.pic5);

上面的“-”负号表示对应的属性值为false

当我们为某个View使用其作为背景色时,会根据状态进行背景图的转换。

public boolean isStateful ()

功能: 表明该状态改变了,对应的drawable图片是否会改变。

注:在StateListDrawable类中,该方法返回为true,显然状态改变后,我们的图片会跟着改变。

知识点二:View的五种状态值

一般来说,Android框架为View定义了四种不同的状态,这些状态值的改变会引发View相关操作,例如:更换背景图片、是否

触发点击事件等;视

视图几种不同状态含义见下图:

其中selected和focused的区别有如下几点:

1,我们通过查看setSelected()方法,来获取相关信息。

SDK中对setSelected()方法----对于与selected状态有如下说明:

public void setSelected (boolean selected)

Since: APILevel 1

Changes the selection state of this view. Aview can be selected or not. Note that selection is not the same as

focus. Views are typically selected in the context of an AdapterView like ListView or GridView ;the selected view is

the view that is highlighted.

Parameters selected   true if the view must be selected, false otherwise

由以上可知:selected不同于focus状态,通常在AdapterView类群下例如ListView或者GridView会使某个View处于

selected状态,并且获得该状态的View处于高亮状态。

2、一个窗口只能有一个视图获得焦点(focus),而一个窗口可以有多个视图处于”selected”状态中。

总结:focused状态一般是由按键操作引起的;

pressed状态是由触摸消息引起的;

selected则完全是由应用程序主动调用setSelected()进行控制。

例如:当我们触摸某个控件时,会导致pressed状态改变;获得焦点时,会导致focus状态变化。于是,我们可以通过这种

更新后状态值去更新我们对应的Drawable对象了。

问题:如何根据状态值的改变去绘制/显示对应的背景图?

当View任何状态值发生改变时,都会调用refreshDrawableList()方法去更新对应的背景Drawable对象。

其整体调用流程如下: View.java类中

[java]  view plain copy print ?

  1. //路径:\frameworks\base\core\java\android\view\View.java

  2. /* Call this to force a view to update its drawable state. This will cause

  3. * drawableStateChanged to be called on this view. Views that are interested

  4. * in the new state should call getDrawableState.

  5. */

  6. //主要功能是根据当前的状态值去更换对应的背景Drawable对象

  7. public void refreshDrawableState() {

  8. mPrivateFlags |= DRAWABLE_STATE_DIRTY;

  9. //所有功能在这个函数里去完成

  10. drawableStateChanged();

  11. }

  12. /* This function is called whenever the state of the view changes in such

  13. * a way that it impacts the state of drawables being shown.

  14. */

  15. // 获得当前的状态属性— 整型集合 ; 调用Drawable类的setState方法去获取资源。

  16. protected void drawableStateChanged() {

  17. //该视图对应的Drawable对象,通常对应于StateListDrawable类对象

  18. Drawable d = mBGDrawable;

  19. if (d != null && d.isStateful()) {  //通常都是成立的

  20. //getDrawableState()方法主要功能:会根据当前View的状态属性值,将其转换为一个整型集合

  21. //setState()方法主要功能:根据当前的获取到的状态,更新对应状态下的Drawable对象。

  22. d.setState(getDrawableState());

  23. }

  24. }

  25. /*Return an array of resource IDs of the drawable states representing the

  26. * current state of the view.

  27. */

  28. public final int[] getDrawableState() {

  29. if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {

  30. return mDrawableState;

  31. } else {

  32. //根据当前View的状态属性值,将其转换为一个整型集合,并返回

  33. mDrawableState = onCreateDrawableState(0);

  34. mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;

  35. return mDrawableState;

  36. }

  37. }

通过这段代码我们可以明白View内部是如何获取更细后的状态值以及动态获取对应的背景Drawable对象----setState()方法

去完成的。这儿我简单的分析下Drawable类里的setState()方法的功能,把流程给走一下:

Step 1 、 setState()函数原型 ,

函数位于:frameworks\base\graphics\java\android\graphics\drawable\StateListDrawable.java 类中

[java]  view plain copy print ?

  1. //如果状态态值发生了改变,就回调onStateChange()方法。

  2. public boolean setState(final int[] stateSet) {

  3. if (!Arrays.equals(mStateSet, stateSet)) {

  4. mStateSet = stateSet;

  5. return onStateChange(stateSet);

  6. }

  7. return false;

  8. }

该函数的主要功能: 判断状态值是否发生了变化,如果发生了变化,就调用onStateChange()方法进一步处理。

Step 2 、onStateChange()函数原型:

该函数位于 frameworks\base\graphics\java\android\graphics\drawable\StateListDrawable.java 类中

[java]  view plain copy print ?

  1. //状态值发生了改变,我们需要找出第一个吻合的当前状态的Drawable对象

  2. protected boolean onStateChange(int[] stateSet) {

  3. //要找出第一个吻合的当前状态的Drawable对象所在的索引位置, 具体匹配算法请自己深入源码看看

  4. int idx = mStateListState.indexOfStateSet(stateSet);

  5. //获取对应索引位置的Drawable对象

  6. if (selectDrawable(idx)) {

  7. return true;

  8. }

  9. }

该函数的主要功能: 根据新的状态值,从StateListDrawable实例对象中,找到第一个完全吻合该新状态值的索引下标处 ;

继而,调用selectDrawable()方法去获取索引下标的当前Drawable对象。

具体查找算法在 mStateListState.indexOfStateSet(stateSet) 里实现了。基本思路是:查找第一个能完全吻合该新状态值

的索引下标,如果找到了,则立即返回。 具体实现过程,只好看看源码咯。

Step 3 、selectDrawable()函数原型:

该函数位于 frameworks\base\graphics\java\android\graphics\drawable\StateListDrawable.java 类中

[java]  view plain copy print ?

  1. public boolean selectDrawable(int idx)

  2. {

  3. if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {

  4. //获取对应索引位置的Drawable对象

  5. Drawable d = mDrawableContainerState.mDrawables[idx];

  6. mCurrDrawable = d; //mCurrDrawable即使当前Drawable对象

  7. mCurIndex = idx;

  8. } else {

  9. }

  10. //请求该View刷新自己,这个方法我们稍后讲解。

  11. invalidateSelf();

  12. return true;

  13. }

该函数的主要功能是选择当前索引下标处的Drawable对象,并保存在mCurrDrawable中。

知识点三: 关于Drawable.Callback接口

该接口定义了如下三个函数:

[java]  view plain copy print ?

  1. //该函数位于 frameworks\base\graphics\java\android\graphics\drawable\Drawable.java 类中

  2. public static interface Callback {

  3. //如果Drawable对象的状态发生了变化,会请求View重新绘制,

  4. //因此我们对应于该View的背景Drawable对象能够”绘制出来”.

  5. public void invalidateDrawable(Drawable who);

  6. //该函数目前还不懂

  7. public void scheduleDrawable(Drawable who, Runnable what, long when);

  8. //该函数目前还不懂

  9. public void unscheduleDrawable(Drawable who, Runnable what);

  10. }

其中比较重要的函数为:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

点击这里免费领取吧!

,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-EcwORLcm-1711325539928)]
[外链图片转存中…(img-t61p3Kj3-1711325539928)]
[外链图片转存中…(img-GHo4cOJp-1711325539928)]
[外链图片转存中…(img-8YbQ6xQ6-1711325539929)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-sJyFJGKw-1711325539929)]

[外链图片转存中…(img-Iw0QQVAv-1711325539929)]

[外链图片转存中…(img-OUQllrld-1711325539930)]

点击这里免费领取吧!

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值