invalidate和requestLayout方法源码分析
invalidate方法源码分析
/**
* Mark the area defined by dirty as needing to be drawn. dirty代表需要重新绘制的脏的区域
* If the view is visible, onDraw(Canvas) will be called at some
point in the future.* This must be called from a UI thread. To call from a non-UI thread, call
postInvalidate().* <b>WARNING:</b> In API 19 and below, this method may be destructive to
dirty.* @param dirty the rectangle矩形 representing表示 the bounds of the dirty region地区
*/
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
//实质还是调用invalidateInternal方法
invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
}
/**
* Invalidate the whole view. 重新绘制整个View
*/
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
//这是所有invalidate的终极调用方法
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {......
// Propagate繁殖、传播 the damage损害的(只需要刷新的) rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//将需要刷新的区域封装到damage中p.invalidateChild(this, damage);
//调用Parent的invalidateChild方法,传递damage给Parent}
......
}
由此可知,View的invalidate方法实质是将要刷新区域直接传递给了【父ViewGroup的invalidateChild方法】,这是一个从当前View向上级父View回溯的过程 。
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
......
do {
......
//循环层层上级调用,直到ViewRootImpl会返回null
parent = parent.invalidateChildInParent(location, dirty);
......
} while (parent != null);
}
这里面主要是一个循环,循环结束的条件是 parent == null,什么情况下parent为null呢?
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
......
//View调用invalidate最终层层上传到ViewRootImpl后最终触发了该方法
scheduleTraversals();
......
return null;
}
也就是上面说的,当层层上级传递到ViewRootImpl的invalidateChildInParent方法时,返回了null,结束了那个do while循环。
postInvalidate方法源码分析
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
//核心,实质就是调用了ViewRootImpl.dispatchInvalidateDelayed方法
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
看见没有,通过ViewRootImpl类的Handler发送了一条MSG_INVALIDATE消息,继续追踪这条消息的处理可以发现:
public void handleMessage(Message msg) {
......
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
......
}
......
}
看见没有,实质就是又在UI Thread中调用了View的invalidate()方法,所以可以说,除了调用所在线程不一样之外,其他的和invalidate()方法都是一样的。
invalidate与postInvalidate方法总结
- 直接调用invalidate方法,请求重新draw,但只会绘制调用者本身。
- 触发setSelection方法,请求重新draw,但只会绘制调用者本身。
- 触发setEnabled方法,请求重新draw,但不会重新绘制任何View包括该调用者本身。
- 触发requestFocus方法,请求View树的draw过程,只绘制"需要重绘"的View。
- 触发setVisibility方法, 当View可视状态在INVISIBLE转换VISIBLE时会间接调用invalidate方法,继而绘制该View。当View的可视状态转换为GONE状态时会间接调用requestLayout和invalidate方法,同时由于View树大小发生了变化,所以会请求measure过程以及draw过程,同样只绘制需要"重新绘制"的视图。
补充performTraversals方法调用时机
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
......
//如果mContentParent为空进行一些初始化
if (mContentParent == null) {
installDecor();
}
......
//把我们的view追加到mContentParent
mContentParent.addView(view, params);
......
}
我们继续看下这个方法中所调用的addView方法,也就是ViewGroup的addView方法,如下:
public void addView(View child) {
addView(child, -1);
}
public void addView(View child, int index) {
......
addView(child, index, params);
}
public void addView(View child, int index, LayoutParams params) {
......
//该方法稍后后面会详细分析
requestLayout();
//重点关注!!!
invalidate(true);
......
}
看见addView调用invalidate方法没有?这不就真相大白了。当我们写一个Activity时,我们一定会通过setContentView方法将我们要展示的界面传入该方法,该方法会将我们的View通过addView追加到id为content的一个FrameLayout(ViewGroup)中,然后addView方法中通过调用invalidate(true)去通知触发ViewRootImpl类的performTraversals()方法,至此递归绘制我们自定义的所有布局。
requestLayout方法源码分析
public void requestLayout() {
......
if (mParent != null && !mParent.isLayoutRequested()) {
//从这个View开始向上一直requestLayout,最终到达ViewRootImpl的requestLayout
mParent.requestLayout();
}
......
}
看见没有,整个和invalidate类似,当我们触发View的requestLayout时其实质就是:层层向上传递,直到ViewRootImpl为止,最后触发ViewRootImpl的requestLayout方法。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//View调用requestLayout最终层层上传到ViewRootImpl后最终触发了该方法
scheduleTraversals();
}
}
看见没有,依旧是和invalidate类似,并且最终调用的也是scheduleTraversals()这个方法,只是在上传过程中所设置的标记不同,最终导致对于View的绘制流程中所触发的方法不同而已。
requestLayout方法总结
- requestLayout()方法会调用【measure】过程和【layout】过程,不会调用【draw】过程,所以不会重新绘制任何View(包括该调用者本身)。
- 使用条件:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求父view重新调用他的onMeasure、onLayout来重新设置自己位置。
- 用途:有时我们在改变一个view 的内容之后,可能会造成显示出现错误,比如写ListView的时候 重用convertview中的某个TextView 可能因为前后填入的text长度不同而造成显示出错,此时我们可以在改变内容之后调用requestLayout方法加以解决。
invalidate和requestLayout方法源码分析的更多相关文章
- Java split方法源码分析
Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...
- Linq分组操作之GroupBy,GroupJoin扩展方法源码分析
Linq分组操作之GroupBy,GroupJoin扩展方法源码分析 一. GroupBy 解释: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值. 查询表达式: var ...
- 【Java】NIO中Selector的select方法源码分析
该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...
- jQuery实现DOM加载方法源码分析
传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... } 但 ...
- jQuery.extend()方法和jQuery.fn.extend()方法源码分析
这两个方法用的是相同的代码,一个用于给jQuery对象或者普通对象合并属性和方法一个是针对jQuery对象的实例,对于基本用法举几个例子: html代码如下: <!doctype html> ...
- jQuery.clean()方法源码分析(一)
在jQuery 1.7.1中调用jQuery.clean()方法的地方有三处,第一次就是在我之前的随笔分析jQuery.buildFramgment()方法里面的,其实还是构造函数的一部分,在处理诸如 ...
- HashMap put、get方法源码分析
HashMap.java的实现是面试必问的问题. JDK版本 java version "1.8.0_91" Java(TM) SE Runtime Environment (bu ...
- HashMap主要方法源码分析(JDK1.8)
本篇从HashMap的put.get.remove方法入手,分析源码流程 (不涉及红黑树的具体算法) jkd1.8中HashMap的结构为数组.链表.红黑树的形式 (未转化红黑树时) (转 ...
- Hystrix微服务容错处理及回调方法源码分析
前言 在 SpringCloud 微服务项目中,我们有了 Eureka 做服务的注册中心,进行服务的注册于发现和服务治理.使得我们可以摒弃硬编码式的 ip:端口 + 映射路径 来发送请求.我们有了 F ...
随机推荐
- Python全栈开发之5、几种常见的排序算法以及collections模块提供的数据结构
转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5492298.html 在面试中,经常会遇到一些考排序算法的题,在这里,我就简单了列举了几种最常见的排序算法供大家学习 ...
- Error:Unable to make the module:***, related gradle configuration was not found. Please, re-import the Gradle project and try again.
打开idea的 View -> Tool Windows -> Gradle.然后点击 Refresh
- ubuntu各种软件安装-装机整套系列
首先声明,本人系统ubuntu 14.04.1 LTS, 以下所有软件均安装于该系统. 一. 首先在windows下删除ubuntu,删除方法如下: 1.进入win7,下载个软件MbrFix,放在C: ...
- 打开tcp_tw_recycle引起的一个问题
今天普空说了一个问题就是如果设置了tcp_tw_recycle ,那么如果客户端是NAT出来的,那么就可能会出现连接被直接rst的情况.然后我google了下,在内核列表也有人说了这个问题 https ...
- nyoj 269 VF 动规
VF 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 Vasya is the beginning mathematician. He decided to make a ...
- BZOJ 4036: [HAOI2015]按位或 集合幂函数 莫比乌斯变换 莫比乌斯反演
http://www.lydsy.com/JudgeOnline/problem.php?id=4036 http://blog.csdn.net/lych_cys/article/details/5 ...
- SpringBoot 如何从前台传递数组
1.SpringBoot 如何从前台传递数组 2.前台 $.ajax({ url: 'deleteBsGiftById', type: 'post', dataType: 'json', data: ...
- [bzoj1022][SHOI2008]小约翰的游戏 John (博弈论)
Description 小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子,在这堆石子中取走任意多的石子,但不能一粒石子也不取 ...
- python开发_linecache
#从linecache的名称,我们可以知道该模块和cache(缓存)有关 #linecache现把文件读入到缓存中,在以后访问文件的时候,就不必要再从硬盘读取 #所以经常用于那些读取频率很高的文件还可 ...
- FPGA LVDS I/O as an Analog Programmable Comparator
http://www.eetimes.com/author.asp?section_id=36&doc_id=1320289 Seeing the new ADC IP being bandi ...