漫画Android:View是怎么绘制出来的?

06-01 1552阅读

漫画Android:View是怎么绘制出来的?

漫画Android:View是怎么绘制出来的?

漫画Android:View是怎么绘制出来的?

漫画Android:View是怎么绘制出来的?

简单来说,View的绘制流程可以概括为三个主要阶段:测量(Measure)、布局(Layout)和绘制(Draw)。

View的绘制是从ViewRootImpl的performTraversals()方法开始,遍历所有视图进行绘制操作:

private void performTraversals() {
     ...............
    //measur过程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     ...............
    //layout过程
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
     ...............
    //draw过程
    performDraw();
}

1. View的测量,计算每个View及其子View的尺寸大小(宽度和高度):

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
      if (mView == null) {
          return;
      }
      try {
          mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      } finally {
          Trace.traceEnd(Trace.TRACE_TAG_VIEW);
      }
}
/**
 * 调用这个方法来算出一个View应该为多大。
 * 实际的测量工作在onMeasure()方法中进行
 */
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  ......
  if (forceLayout || needsLayout) {
    .....
    // 忽略缓存,则调用onMeasure()重新进行测量工作
    int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
    if (cacheIndex  

2. View的布局,确定View在父控件里的布局位置(左、上、右、下边距):

 private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        .........
        final View host = mView;
        if (host == null) {
            return;
        }
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        .........
}
public void layout(int l, int t, int r, int b) {
    .......
    //调用onLayout(),ViewGroup须重写此方法
    onLayout(changed, l, t, r, b);
    .......
}

3. View的绘制,将View的内容真正绘制到屏幕上,绘制背景、canvas图层、内容、子view、padding边缘和装饰:

public void draw(Canvas canvas) {
     ........
    // 绘制背景
    drawBackground(canvas);
    // 绘制内容
    onDraw(canvas);
    // 绘制子View
    dispatchDraw(canvas);
    // 绘制装饰,如scrollBar
    onDrawForeground(canvas)
    ........
}

总结流程图:

                  +-------------------+
                  |   ViewRootImpl    |
                  +---------+---------+
                            |
                            | 调用 performTraversals()
                            v
                  +---------+---------+
                  |    Measure Phase  |
                  |     (测量阶段)     |
                  +---------+---------+
                            |
                            | 递归调用 measure() -> onMeasure()
                            v
                  +---------+---------+
                  |   Layout Phase    |
                  |     (布局阶段)     |
                  +---------+---------+
                            |
                            | 递归调用 layout() -> onLayout()
                            v
                  +---------+---------+
                  |    Draw Phase     |
                  |      (绘制阶段)     |
                  +---------+---------+
                            |
                            | 递归调用 draw() -> onDraw()/dispatchDraw()
                            v
                  +-------------------+
                  |   将内容渲染到屏幕   |
                  +-------------------+

漫画Android:View是怎么绘制出来的?

漫画Android:View是怎么绘制出来的?

View的刷新机制核心方法:invalidate() 和 requestLayout()

  1. invalidate():触发重绘

    标记View为“失效”状态,表示View的内容需要重新绘制。

    触发时机: 当View的外观(如颜色、文本、图片)发生变化,但尺寸和位置不变时,通常调用invalidate()。

public void invalidate() {  
    ………… 
  
    if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {  
        mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;  
        final ViewParent p = mParent;  
        final AttachInfo ai = mAttachInfo;  
        if (p != null && ai != null) {  
            final Rect r = ai.mTmpInvalRect;  
            r.set(0, 0, mRight - mLeft, mBottom - mTop);  
            // Don't call invalidate -- we don't want to internally scroll  
            // our own bounds  
            p.invalidateChild(this, r);  
        }  
    }  
}  

可以看到,View首先通过成员变量mParent记录自己的父View,然后将AttachInfo中保存的信息告诉父View来刷新自己。

invalidate()是一种相对轻量级的刷新方式,因为它只涉及重绘,不会重新计算尺寸和位置。

  1. requestLayout():触发重新测量和布局

    标记View的尺寸或位置需要重新计算。

    触发时机: 当View的尺寸或位置发生变化时,通常调用requestLayout()。

// View类中的 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;
    }
    // 增加PFLAG_FORCE_LAYOUT标记,在measure时会校验此属性
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;
    // 父类不为空&&父类没有请求重新布局(是否有PFLAG_FORCE_LAYOUT标志)
    if (mParent != null && !mParent.isLayoutRequested()) {
        // 调用父类的requestLayout
        mParent.requestLayout();
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null;
    }
}

requestLayout()会沿着View树向上查找,直到找到一个需要重新测量和布局的父View。

ViewRoot的实现类为ViewRootImpl,在这个类中的requestLayout方法与View中的并不相同:

public void requestLayout() {
    // 是否在处理requestLayout
    if (!mHandlingLayoutInLayoutRequest) {
        // 检查创建view的线程是否为当前线程
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals(); // scheduleTraversals方法调用performTraversals方法
    }
}

最终,会调用到ViewRootImpl的requestLayout()方法。

在下一个VSync信号到来时,如果检测到有View需要重新布局,系统会从ViewRootImpl开始,重新执行完整的「测量(Measure)、布局(Layout)和绘制(Draw)」三阶段。

VSync信号同步: Android系统会以每秒60帧的速度(16.67ms一帧)发送VSync(Vertical Synchronization)信号。

requestLayout()是一种相对重量级的刷新方式,因为它会触发完整的测量、布局和绘制流程,可能会导致性能开销,尤其是在复杂的布局中。

漫画Android:View是怎么绘制出来的?

postInvalidate()是invalidate()的异步版本。

invalidate()是在主线程中调用的,所以如果要在子线程中使用就要使用Handler机制,而postInvalidate()则可在直接在子线程和主线程中使用来刷新视图。

漫画Android:View是怎么绘制出来的?

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码