概述

在我们在进行自定义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的更多相关文章

  1. Android View重绘和更新: invalidate和requestLayout 总结的不错 赶紧复制。。哈哈

    总述:View有两个很重要的方法:invalidate和requestLayout,常用于View重绘和更新. Invalidate:To farce a view to draw,call inva ...

  2. android Dialog重绘

    String title = ""; if(itemInfo!=null) title = "\n\""+itemInfo.itemSSID+&quo ...

  3. Android视图状态及重绘流程分析,带你一步步深入了解View(三)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...

  4. View (四)视图状态及重绘流程分析

    相 信大家在平时使用View的时候都会发现它是有状态的,比如说有一个按钮,普通状态下是一种效果,但是当手指按下的时候就会变成另外一种效果,这样才会给 人产生一种点击了按钮的感觉.当然了,这种效果相信几 ...

  5. Android View的重绘过程之Draw

    博客首页:http://www.cnblogs.com/kezhuang/p/ View绘制的三部曲,测量,布局,绘画现在我们分析绘画部分测量和布局 在前两篇文章中已经分析过了.不了解的可以去我的博客 ...

  6. Android invalidate()方法 requestLayout()方法分析

    强调一点的就是,在onMeasure(),onLayout(),onDraw()这三个流程中,Google已经帮我们把draw()过程框架已经写好了,自定义的ViewGroup只需要实现 measur ...

  7. MFC重绘函数:InvalidateRect(), Invalidate()和UpdateWindow()

    1. 重绘消息 当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化) WM_NCPAINT系统会自己搞定 WM_PAINT消 ...

  8. 《Android内核剖析》读书笔记 第13章 View工作原理【View重绘过程】

    计算视图大小的过程(Measure) 视图大小,准确的来说应该是指视图的布局大小:我们在layout.xml中为每个UI控件设置的layout_width/layout_height两个属性被用来设置 ...

  9. Android深入研究Adapter重绘

    一直以来Adapter的使用都仅仅是流于表面,仅仅知道要实现几个抽象的方法,把Adapter设置给某种listView,就能够非常好的工作起来.所谓理解仅仅是建立在主观的猜想上面,认为应该是这样,对, ...

随机推荐

  1. Ionic1开发环境配置ji

    配置Ionic1开发环境环境:windows7 32位+jdk1.8+ionic1.3,64位系统可以参考下面方法,软件注意选择对应的版本即可.    1.下载JDK并配置Java运行环境       ...

  2. 项目中用到的node-express模块

    反向代理中间件: var proxyMiddleWare = require("http-proxy-middleware"); var proxyPath = "htt ...

  3. numpy pandas 索引注意事项

    pandas.DataFrame 的 iloc # ------------------------------------------------------------ 'python式的切片,包 ...

  4. 打印十字图 JAVA 递归实现

    这个是我自己想的,头疼了一个下午,不过还好.做出来了.在网上找这道题但没有找到用递归的做法. /*递归思想实现 * 标题:打印十字图 小明为某机构设计了一个十字型的徽标(并非红十字会啊),如下所示(可 ...

  5. alpha-咸鱼冲刺day1

    一,合照 emmmmm.自然是没有的. 二,项目燃尽图 三,项目进展 登陆界面随意写了一下.(明天用来做测试的) 把学姐给我的模板改成了自家的个人主页界面,侧边栏啥的都弄出来了(快撒花花!) 四,问题 ...

  6. 201621123062《java程序设计》第14周作业总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 思维导图: 2. 使用数据库技术改造你的系统 2.1 简述如何使用数据库技术改造你的系统.要建立什么表?截图你的 ...

  7. 201621123025《Java程序设计》第1周学习总结

    201621123025<Jave程序设计>第一周学习总结 1.本章学习总结 对于java这门课程,如果不会编码那么会很难学会如何去使用它,而在大一的一二学期的专业课--C语言和数据结构我 ...

  8. bzoj 2962 序列操作

    2962: 序列操作 Time Limit: 50 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 有一个长度为n的序列, ...

  9. Scala 集合入门

    1. 数组 1.1 定长数组 scala.Array 是定长的可变的索引型集合, JVM 中, Scala 的 Array 是以 Java 数组方式实现. String 对应 java.lang.St ...

  10. es6+react.js组件入门初探

    React是一个用于构建用户见面的javascript库. React主要用于构建UI,许多人认为React是MVC中的V(视图) React起源于Facebook的内部项目,用来架设Instagra ...