Android中View绘制各种状态的背景图片原理深入分析以及StateListDrawable使用
/* 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使用相关推荐
- Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解...
2019独角兽企业重金招聘Python工程师标准>>> 今天继续给大家分享下View的相关知识,重点有一下两点: 1.View的几种不同状态属性 2.如何根 ...
- Android中View绘制流程以及invalidate()等相关方法分析
...
- android view 绘制过程,深入理解Android中View绘制的三大流程
前言 最近对Android中View的绘制机制有了一些新的认识,所以想记录下来并分享给大家.View的工作流程主要是指measure.layout.draw这三大流程,即测量.布局和绘制,其中meas ...
- Android中View绘制流程
2019独角兽企业重金招聘Python工程师标准>>> 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程 ...
- Android中View绘制流程分析
创建Window 在Activity的attach方法中通过调用PolicyManager.makeNewWindo创建Window,将一个View add到WindowManager时,Window ...
- Android中的ImageView的getDrawableCache获取背景图片的时候注意的问题
获取ImageView的背景图片使用getDrawableCache方法,不要使用getDrawable方法,后者获取不到图片的. 1.在调用imageView.getDrawableCache()之 ...
- Android中View绘制优化二一---- 使用include /标签复用布局文件
本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 译二: 使用<include />标签复用布局文件 翻译地址:http://developer. ...
- Android 中View的绘制机制源代码分析 三
到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...
- Android之View绘制流程源码分析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于稍有自定义View经验的安卓开发者来说,onMeasure,onLayout,onDraw这三个方法都不会陌生,起码多少都有所接触吧. 在安卓中 ...
最新文章
- 使用Bioconda管理生信软件(以bwa为例)
- 12种超酷HTML5 SVG和CSS3浮动标签效果
- java调用ole ie_ActiveX (.ocx)的写法,及在IE里调用
- LookAheadEnumerator:在解析器中实现回溯
- java netty 面试_Java 200+ 面试题补充② Netty 模块
- JS页面打开方式丶对话框及页面跳转方式
- 一个注册表清理工具Advanced Uninstaller PRO 12
- 小程序tabBar图标显示太大
- 测试进阶必备,这5款http接口自动化测试工具真的很香
- 高端存储架构的前世今生
- java 计算正态分布_统计基本概念 期望 方差 均方差 正态分布 Java统计计算
- 机器学习 主成分分析(Principal Component Analysis)
- Unity 3D 入门小游戏 小球酷跑(上)
- cve 爬虫_爬虫技术实践(九)国家信息安全漏洞库基于月份的漏洞收集实战
- warning:In file included from...
- vue播放视频使用原生video标签基本功能(不含样式)
- 基于云服务创建离线数据统计分析服务(一)
- 【SpringCloud】ZuulFilter过滤器
- 求斐波那契数列c++实现
- 从今天开始—兄弟连IT教育