前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >View相关问题再探

View相关问题再探

作者头像
码上积木
发布2020-11-24 15:30:41
3680
发布2020-11-24 15:30:41
举报
文章被收录于专栏:码上积木码上积木

昨天发了关于线程更新UI的一些内容,其中关于View的一些问题还是比较重要的。我们再来回顾下,并提出一些问题。

Activity从创建到我们看到界面,发生了哪些事

  • 首先是通过setContentView加载布局,这其中创建了一个DecorView,然后根据然后根据activity设置的主题(theme)或者特征(Feature)加载不同的根布局文件,最后再通过inflate方法加载layoutResID资源文件,其实就是解析了xml文件,根据节点生成了View对象。流程图:

加载布局流程

  • 其次就是进行view绘制到界面上,这个过程发生在handleResumeActivity方法中,也就是触发onResume的方法。在这里会创建一个ViewRootImpl对象,作为DecorView的parent然后对DecorView进行测量布局和绘制三大流程。流程图:

绘制流程

Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?

  • PhoneWindow是Window 的唯一子类,每个Activity都会创建一个PhoneWindow对象,你可以理解它为一个窗口,但不是真正的可视窗口,而是一个管理类,是Activity和整个View系统交互的接口,是Activity和View交互系统的中间层。
  • DecorView是PhoneWindow的一个内部类,是整个View层级的最顶层,一般包括标题栏和内容栏两部分,会根据不同的主题特性调整不同的布局。它是在setContentView方法中被创建,具体点来说是在PhoneWindow的installDecor方法中被创建。
  • ViewRootImpl是DecorView的parent,用来控制View的各种事件,在handleResumeActivity方法中被创建。

requestLayout和invalidate

  • requestLayout方法是用来触发绘制流程,他会会一层层调用 parent 的requestLayout,一直到最上层也就是ViewRootImpl的requestLayout,这里也就是判断线程的地方了,最后会执行到performMeasure -> performLayout -> performDraw 三个绘制流程,也就是测量——布局——绘制。
代码语言:javascript
复制
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();//执行绘制流程
        }
    }

其中performMeasure方法会执行到View的measure方法,用来测量大小。performLayout方法会执行到view的layout方法,用来计算位置。performDraw方法需要注意下,他会执行到view的draw方法,但是并不一定会进行绘制,只有只有 flag 被设置为 PFLAG_DIRTY_OPAQUE 才会进行绘制。

  • invalidate方法也是用来触发绘制流程,主要表现就是会调用draw()方法。虽然他也会走到scheduleTraversals方法,也就是会走到三大流程,但是View会通过mPrivateFlags来判断是否进行onMeasure和onLayout操作。而在用invalidate方法时,更新了mPrivateFlags,所以不会进行measure和layout。同时他也会设置Flag为PFLAG_DIRTY_OPAQUE,所以肯定会执行onDraw方法。
代码语言:javascript
复制

private void invalidateRectOnScreen(Rect dirty) {
        final Rect localDirty = mDirty;
        //...
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            scheduleTraversals();//执行绘制流程
        }
    }

最后看一下scheduleTraversals方法中三大绘制流程逻辑,是不是我们之前说的那样,FORCE_LAYOUT标志才会onMeasure和onLayout,PFLAG_DIRTY_OPAQUE标志才会onDraw:

代码语言:javascript
复制

  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
    // 只有mPrivateFlags为PFLAG_FORCE_LAYOUT的时候才会进行onMeasure方法
    if (forceLayout || needsLayout) {
      onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    // 设置 LAYOUT_REQUIRED flag
    mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
  }


  public void layout(int l, int t, int r, int b) {
    ...
    //判断标记位为PFLAG_LAYOUT_REQUIRED的时候才进行onLayout方法
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
     }
 }



public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    // flag 是 PFLAG_DIRTY_OPAQUE 则需要绘制
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
            (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
    if (!dirtyOpaque) {
        drawBackground(canvas);
    }
    if (!dirtyOpaque) onDraw(canvas);
    // 绘制 Child
    dispatchDraw(canvas);
    // foreground 不管 dirtyOpaque 标志,每次都会绘制
    onDrawForeground(canvas);
} 


参考文章中有一段总结挺好的:

虽然两者都是用来触发绘制流程,但是在measure和layout过程中,只会对 flag 设置为 FORCE_LAYOUT 的情况进行重新测量和布局,而draw方法中只会重绘flag为 dirty 的区域。 requestLayout 是用来设置FORCE_LAYOUT标志,invalidate 用来设置 dirty 标志。所以 requestLayout 只会触发 measure 和 layout,invalidate 只会触发 draw。

参考

https://mp.weixin.qq.com/s/wy9V4wXUoEFZ6ekzuLJySQ

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Activity从创建到我们看到界面,发生了哪些事
  • Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?
  • requestLayout和invalidate
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档