Android视图重绘,使用invalidate还是requestLayout
概述
在我们在进行自定义View的相关开发中,当我们更改了当前View的状态,比如大小,位置等,我们需要重新刷新整个界面,保证显示最新的状态。在Android中,让当前的视图重绘有两种方式,invalidate和requestLayout,今天我们看看这两种方式的原理以及区别。
分析
invalidate的原理
public void invalidate() {
invalidate(true);
}
最后会调用到invalidateInternal这个方法
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
} if (skipInvalidate()) {
return;
} if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
} mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
} // 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);
p.invalidateChild(this, damage);
}
.....
我们看到方法的最后调用了ViewParent的invalidateChild方法,因为ViewParent是个接口,invalidateChild是空实现,我们去看看它的实现类ViewRootImpl中的invalidateChild是如何做的
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
} if (mCurScrollY != || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != ) {
dirty.offset(, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-, -);
}
} invalidateRectOnScreen(dirty); return null;
}
又会调用ViewRootImpl中的invalidate方法
void invalidate() {
mDirty.set(, , mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
这里调用了scheduleTraversals重新开始了View的绘制,我们知道View的绘制是从ViewRootImpl的performTraversals方法开始的。我们看看scheduleTraversals是不是触发了performTraversals。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
在scheduleTraversals方法中我们发现了一个mTraversalRunnable对象,这个对象就是我们要观察的重点
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
我们发现这个对象就是一个Runnable对象,我们在scheduleTraversals方法中传入mTraversalRunnable 就会执行run方法,其中又调用了doTraversal这个方法
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
最后我们发现在doTraversal方法中调用了performTraversals开始了View的重新绘制,这就是invalidate的整个过程。
requestLayout的原理
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
其中会调用ViewParent的requestLayout方法,同样,我们去看ViewRootImpl中的requestLayout方法。
Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
这里调用了scheduleTraversals,后面的步骤就和上面invalidate时一样了。相对来说,requestLayout的流程还是比较简单的。
区别
既然两种方式都可以完成View的重绘,那么有什么区别呢?
使用invalidate重绘当前视图是不会再次执行measure和layout流程的。因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。
如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了

Android视图重绘,使用invalidate还是requestLayout的更多相关文章
- Android View重绘和更新: invalidate和requestLayout 总结的不错 赶紧复制。。哈哈
总述:View有两个很重要的方法:invalidate和requestLayout,常用于View重绘和更新. Invalidate:To farce a view to draw,call inva ...
- android Dialog重绘
String title = ""; if(itemInfo!=null) title = "\n\""+itemInfo.itemSSID+&quo ...
- Android视图状态及重绘流程分析,带你一步步深入了解View(三)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...
- View (四)视图状态及重绘流程分析
相 信大家在平时使用View的时候都会发现它是有状态的,比如说有一个按钮,普通状态下是一种效果,但是当手指按下的时候就会变成另外一种效果,这样才会给 人产生一种点击了按钮的感觉.当然了,这种效果相信几 ...
- Android View的重绘过程之Draw
博客首页:http://www.cnblogs.com/kezhuang/p/ View绘制的三部曲,测量,布局,绘画现在我们分析绘画部分测量和布局 在前两篇文章中已经分析过了.不了解的可以去我的博客 ...
- Android invalidate()方法 requestLayout()方法分析
强调一点的就是,在onMeasure(),onLayout(),onDraw()这三个流程中,Google已经帮我们把draw()过程框架已经写好了,自定义的ViewGroup只需要实现 measur ...
- MFC重绘函数:InvalidateRect(), Invalidate()和UpdateWindow()
1. 重绘消息 当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化) WM_NCPAINT系统会自己搞定 WM_PAINT消 ...
- 《Android内核剖析》读书笔记 第13章 View工作原理【View重绘过程】
计算视图大小的过程(Measure) 视图大小,准确的来说应该是指视图的布局大小:我们在layout.xml中为每个UI控件设置的layout_width/layout_height两个属性被用来设置 ...
- Android深入研究Adapter重绘
一直以来Adapter的使用都仅仅是流于表面,仅仅知道要实现几个抽象的方法,把Adapter设置给某种listView,就能够非常好的工作起来.所谓理解仅仅是建立在主观的猜想上面,认为应该是这样,对, ...
随机推荐
- macOS下利用dSYM文件将crash文件中的内存地址转换为可读符号
一.使用流程 Windows下的程序运行崩溃时,往往可以利用pdb文件快速解析出程序崩溃的具体位置,甚至可以对应到源代码的具体行数.macOS下的symbolicatecrash也具备相应的功能.对应 ...
- Java集合详解及List源码分析
对于数组我们应该很熟悉,一个数组在内存中总是一块连续的存储空间,数组的创建使用new关键字,数组是引用类型的数据,一旦第一个元素的位置确定,那么后面的元素位置也就确定了,数组有一个最大的局限就是数组一 ...
- Lucene-01:创建索引
我们在D盘下建一个文件夹叫lucene,lucene内再建两个文件夹,一个叫example,一个叫index01.example文件夹下三个txt文件,a.txt内容为hello java,b.txt ...
- 初识mango DB
换工作了,第一次接触到mango数据库,有点云里雾里,整理一篇最基本的增删该查语句 百度百科说mango DB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据 ...
- CXF对Interceptor拦截器的支持
前面在Axis中介绍过Axis的Handler,这里CXF的Interceptor就和Handler的功能类似.在每个请求响应之前或响应之后,做一些事情.这里的Interceptor就和Filter. ...
- Redis这些知识点,是必须知道的!
Redis是一个开源(BSD许可)的内存数据结构存储,可作为数据库,缓存和消息队列.相比Memcached它支持更多的数据结构,如string(字符串),hash(哈希),list(链表),set(集 ...
- 利用whoosh对mongoDB的中文文档建立全文检索
1.建立索引 #coding=utf-8 from __future__ import unicode_literals __author__ = 'zh' import sys,os from wh ...
- Jmeter中正则表达式提取器使用详解
在使用Jmeter过程中,会经常使用到正则表达式提取器提取器,虽然并不直接涉及到请求的测试,但是对于数据的传递起着很大的作用,本篇博文就是主要讲解关于正则表达式及其在Jmeter的Sampler中的调 ...
- android 滑动分页
import android.app.ListActivity;import android.os.Bundle;import android.os.Handler;import android.vi ...
- 对lua表中数据按一定格式处理,循环
function putStartCard(handCard) function dataDeal(array,a,b,c) cclog("进入datadeal=============== ...