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

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

  • in the new state should call getDrawableState.

*/

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

public void refreshDrawableState() {

mPrivateFlags |= DRAWABLE_STATE_DIRTY;

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

drawableStateChanged();

}

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

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

*/

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

protected void drawableStateChanged() {

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

Drawable d = mBGDrawable;

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

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

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

d.setState(getDrawableState());

}

}

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

  • current state of the view.

*/

public final int[] getDrawableState() {

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

return mDrawableState;

} else {

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

mDrawableState = onCreateDrawableState(0);

mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;

return mDrawableState;

}

}

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

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

Step 1 、 setState()函数原型 ,

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

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

public boolean setState(final int[] stateSet) {

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

mStateSet = stateSet;

return onStateChange(stateSet);

}

return false;

}

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

Step 2 、onStateChange()函数原型:

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

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

protected boolean onStateChange(int[] stateSet) {

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

int idx = mStateListState.indexOfStateSet(stateSet);

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

if (selectDrawable(idx)) {

return true;

}

}

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

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

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

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

Step 3 、selectDrawable()函数原型:

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

public boolean selectDrawable(int idx)

{

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

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

Drawable d = mDrawableContainerState.mDrawables[idx];

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

mCurIndex = idx;

} else {

}

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

invalidateSelf();

return true;

}

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

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

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

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

public static interface Callback {

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

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

public void invalidateDrawable(Drawable who);

//

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

//

public void unscheduleDrawable(Drawable who, Runnable what);

}

其中比较重要的函数为:

public voidinvalidateDrawable(Drawable who)

函数功能:如果Drawable对象的状态发生了变化,会请求View重新绘制,因此我们对应于该View的背景Drawable对象

能够重新”绘制“出来。

Android框架View类继承了该接口,同时实现了这三个函数的默认处理方式,其中invalidateDrawable()方法如下:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource

{

//Invalidates the specified Drawable.

//默认实现,重新绘制该视图本身

public void invalidateDrawable(Drawable drawable) {

if (verifyDrawable(drawable)) { //是否是同一个Drawable对象,通常为真

final Rect dirty = drawable.getBounds();

final int scrollX = mScrollX;

final int scrollY = mScrollY;

//重新请求绘制该View,即重新调用该View的draw()方法 …

invalidate(dirty.left + scrollX, dirty.top + scrollY,

dirty.right + scrollX, dirty.bottom + scrollY);

}

}

}

因此,我们的Drawable类对象必须将View设置为回调对象,否则,即使改变了状态,也不会显示对应的背景图。 如下:

Drawable d  ;                // 图片资源

d.setCallback(View v) ;  // 视图v的背景资源为 d 对象

知识点四:View绘制背景图片过程

在前面的博客中《Android中View绘制流程以及invalidate()等相关方法分析》,我们知道了一个视图的背景绘制过程时在

View类里的draw()方法里完成的,我们这儿在回顾下draw()的流程,同时重点讲解下绘制背景的操作。

//方法所在路径:frameworks\base\core\java\android\view\View.java

//draw()绘制过程

private void draw(Canvas canvas){

//该方法会做如下事情

//1 、绘制该View的背景

//其中背景图片绘制过程如下:

//是否透明, 视图通常是透明的 , 为true

if (!dirtyOpaque) {

//开始绘制视图的背景

final Drawable background = mBGDrawable;

if (background != null) {

final int scrollX = mScrollX; //获取偏移值

final int scrollY = mScrollY;

//视图的布局坐标是否发生了改变, 即是否重新layout了。

if (mBackgroundSizeChanged) {

//如果是,我们的Drawable对象需要重新设置大小了,即填充该View。

background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);

mBackgroundSizeChanged = false;

}

//View没有发生偏移

if ((scrollX | scrollY) == 0) {

background.draw(canvas); //OK, 该方法会绘制当前StateListDrawable的当前背景Drawable

} else {

//View发生偏移,由于背景图片值显示在布局坐标中,即背景图片不会发生偏移,只有视图内容onDraw()会发生偏移

//我们调整canvas对象的绘制区域,绘制完成后对canvas对象属性调整回来

canvas.translate(scrollX, scrollY);

background.draw(canvas); //OK, 该方法会绘制当前StateListDrawable的当前背景Drawable

canvas.translate(-scrollX, -scrollY);

}

}

}

//2、为绘制渐变框做一些准备操作

//3、调用onDraw()方法绘制视图本身

//4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中。

//5、绘制渐变框

}

That’s all ! 我们用到的知识点也就这么多吧。 如果大家有丝丝不明白的话,可以去看下源代码,具体去分析下这些流程到底

是怎么走下来的。

我们从宏观的角度分析了View绘制不同状态背景的原理,View框架就是这么做的。为了易于理解性,

下面我们通过一个小Demo来演示前面种种流程。

Demo 说明:

我们参照View框架中绘制不同背景图的实现原理,自定义一个View类,通过给它设定StateListDrawable对象,使其能够在

不同状态时能动态"绘制"背景图片。 基本流程方法和View.java类实现过程一模一样。

截图如下:

            

初始背景图                                                            触摸后显示的背景图(pressed)

一、主文件MainActivity.java如下:

/**

  • @author http://http://blog.csdn.net/qinjuning

*/

public class MainActivity extends Activity

{

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

LinearLayout ll = new LinearLayout(MainActivity.this);

CustomView customView = new CustomView(MainActivity.this);

//简单设置为 width 200px - height 100px吧

ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(200 , 100);

customView.setLayoutParams(lp);

//需要将该View设置为可点击/触摸状态,否则触摸该View没有效果。

customView.setClickable(true);

ll.addView(customView);

setContentView(ll);

}

}

功能很简单,为Activity设置了视图 。

二、 自定义View如下 , CustomView.java :

/**

  • @author http://http://blog.csdn.net/qinjuning

*/

//自定义View

public class CustomView extends View /extends Button/

{

private static St

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整资料开源分享

ring TAG = “TackTextView”;

private Context mContext = null;

private Drawable mBackground = null;

private boolean mBGSizeChanged = true;; //视图View布局(layout)大小是否发生变化

总结

笔者之前工作是在金融公司可能并不是特别追求技术,而笔者又是喜欢追求技术的人,所以格格不入,只能把目标放在互联网大厂了。也希望大家都去敢于尝试和追逐自己的梦想!
BATJ大厂Android高频面试题

觉得有收获的记得点赞,关注+收藏哦!你们的点赞就是我的动力!

{

private static St

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整资料开源分享

ring TAG = “TackTextView”;

private Context mContext = null;

private Drawable mBackground = null;

private boolean mBGSizeChanged = true;; //视图View布局(layout)大小是否发生变化

总结

笔者之前工作是在金融公司可能并不是特别追求技术,而笔者又是喜欢追求技术的人,所以格格不入,只能把目标放在互联网大厂了。也希望大家都去敢于尝试和追逐自己的梦想!
BATJ大厂Android高频面试题

[外链图片转存中…(img-SHkUQDbm-1640924461176)]

[外链图片转存中…(img-5N4K2y5O-1640924461177)]

[外链图片转存中…(img-S8s7aWpi-1640924461177)]

[外链图片转存中…(img-W5cSduq1-1640924461178)]

觉得有收获的记得点赞,关注+收藏哦!你们的点赞就是我的动力!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

Android中View绘制各种状态的背景图片原理深入分析以及StateListDrawable使用相关推荐

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

    2019独角兽企业重金招聘Python工程师标准>>> 今天继续给大家分享下View的相关知识,重点有一下两点:   1.View的几种不同状态属性            2.如何根 ...

  2. Android中View绘制流程以及invalidate()等相关方法分析

                                                                                                        ...

  3. android view 绘制过程,深入理解Android中View绘制的三大流程

    前言 最近对Android中View的绘制机制有了一些新的认识,所以想记录下来并分享给大家.View的工作流程主要是指measure.layout.draw这三大流程,即测量.布局和绘制,其中meas ...

  4. Android中View绘制流程

    2019独角兽企业重金招聘Python工程师标准>>> 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程 ...

  5. Android中View绘制流程分析

    创建Window 在Activity的attach方法中通过调用PolicyManager.makeNewWindo创建Window,将一个View add到WindowManager时,Window ...

  6. Android中的ImageView的getDrawableCache获取背景图片的时候注意的问题

    获取ImageView的背景图片使用getDrawableCache方法,不要使用getDrawable方法,后者获取不到图片的. 1.在调用imageView.getDrawableCache()之 ...

  7. Android中View绘制优化二一---- 使用include /标签复用布局文件

    本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 译二: 使用<include />标签复用布局文件  翻译地址:http://developer. ...

  8. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  9. Android之View绘制流程源码分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于稍有自定义View经验的安卓开发者来说,onMeasure,onLayout,onDraw这三个方法都不会陌生,起码多少都有所接触吧. 在安卓中 ...

最新文章

  1. 使用Bioconda管理生信软件(以bwa为例)
  2. 12种超酷HTML5 SVG和CSS3浮动标签效果
  3. java调用ole ie_ActiveX (.ocx)的写法,及在IE里调用
  4. LookAheadEnumerator:在解析器中实现回溯
  5. java netty 面试_Java 200+ 面试题补充② Netty 模块
  6. JS页面打开方式丶对话框及页面跳转方式
  7. 一个注册表清理工具Advanced Uninstaller PRO 12
  8. 小程序tabBar图标显示太大
  9. 测试进阶必备,这5款http接口自动化测试工具真的很香
  10. 高端存储架构的前世今生
  11. java 计算正态分布_统计基本概念 期望 方差 均方差 正态分布 Java统计计算
  12. 机器学习 主成分分析(Principal Component Analysis)
  13. Unity 3D 入门小游戏 小球酷跑(上)
  14. cve 爬虫_爬虫技术实践(九)国家信息安全漏洞库基于月份的漏洞收集实战
  15. warning:In file included from...
  16. vue播放视频使用原生video标签基本功能(不含样式)
  17. 基于云服务创建离线数据统计分析服务(一)
  18. 【SpringCloud】ZuulFilter过滤器
  19. 求斐波那契数列c++实现
  20. 从今天开始—兄弟连IT教育

热门文章

  1. flatten(扁平化)数组
  2. oracle求非偶非素数的和,Sub Maths__写给非数学专业的朋友们
  3. JS高级程序设计读书笔记(第五章 引用变量)
  4. 取消UL和OL符号以及padding和margin后恢复默认值的CSS
  5. 2022蓝桥杯A组Python
  6. 怎么查哪些期刊是核心,哪些不是,EI有哪些等等问题
  7. 核心大小1M的VIN码OCR识别核心
  8. 飞桨框架v2.3 API最新升级 | 对科学计算、概率分布和稀疏Tensor等提供更全面支持
  9. java保存图片_java 保存图片
  10. 关于HTML中常用选择器