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 somepoint in the future.* This must be called from a UI thread. To call from a non-UI thread, callpostInvalidate().* <b>WARNING:</b> In API 19 and below, this method may be destructive todirty.* @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会返回nullparent = parent.invalidateChildInParent(location, dirty);......} while (parent != null);}
这里面主要是一个循环,循环结束的条件是 parent == null,什么情况下parent为null呢?
@Overridepublic 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 windowfinal 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方法调用时机
@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {......//如果mContentParent为空进行一些初始化if (mContentParent == null) {installDecor();}......//把我们的view追加到mContentParentmContentParent.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的requestLayoutmParent.requestLayout();}......}
看见没有,整个和invalidate类似,当我们触发View的requestLayout时其实质就是:层层向上传递,直到ViewRootImpl为止,最后触发ViewRootImpl的requestLayout方法。
@Overridepublic 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 ...
随机推荐
- HTML5 Video/Audio播放本地文件
这段时间经常看到开发者在反复询问同一个问题,为什么通过设置src属性,不能播放本地的媒体文件?例如video.src=”D:\test.mp4”. 这是因为浏览器中的JavaScript不能直接直接访 ...
- CodeForcs 797E Array Queries
$dp$预处理,暴力. 如果$k > sqrt(n)$,那么答案不会超过$sqrt(n)$,暴力模拟即可.如果$k <= sqrt(n)$,那么可以$dp$预处理打表. #include ...
- async await 使用笔记
JavaScript的网络请求异步的,即网络请求不会阻塞当前 js 代码的继续执行,而是通过回调的方式,网络请求的代码块中注入回调函数,当网络请求完成,会触发相应的事件,通过触发事件来执行注册的回调函 ...
- Linux中mysql的操作
一.mysql服务操作 二.数据库操作 三.数据表操作 四.修改表结构 五.数据备份和恢复 六.卸载数据库
- 【UOJ #206】【APIO 2016】Gap
http://uoj.ac/problem/206 对于T=1,直接从两端往中间跳可以遍历所有的点. 对于T=2,先求出最小值a和最大值b,由鸽巢原理,答案一定不小于\(\frac{b-a}{N-1} ...
- Linux的十个最危险的命令
Linux命令行佷有用.很高效,也很有趣,但有时候也很危险,尤其是在你不确定你自己在正在做什么时候. 这篇文章将会向你介绍十条命令,但你最好不要尝试着去使用. 当然,以下命令通常都是在root权限下才 ...
- 记一次初步Linux提权
前言. 提权这么久了 还是头一次提下Linux的服务器... 由于之前一直钻研的win服务器 要不是前些日子爆出来Struts2-045漏洞 估计还没时间接触Linux提权.... 正文. st2 ...
- [HAOI2015]数组游戏
题目大意: 有一排n个格子,每个格子上都有一个白子或黑子,在上面进行游戏,规则如下: 选择一个含白子的格子x,并选择一个数k,翻转x,2x,...,kx格子上的子. 不能操作者负. 思路: 将“某个格 ...
- 【BZOJ2763/洛谷p4563】【分层图最短路】飞行路线
2763: [JLOI2011]飞行路线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 4630 Solved: 1797[Submit][Stat ...
- 【转】如何修改maven工程jdk版本
1.使用maven的时候,默认会使用1.5版本的JDK,并且也是编译成1.5的,我的电脑里面用的JDK是1.7的,1.8也出来了,没理由还用1.5的吧!所以我手动改成了1.7,郁闷的是,每次 mave ...