【Android】言简意赅的View绘制流程

一、基本概念:

1、Activity、Window、PhoneWindow、DecorView之间的关系:

Activity:很熟悉了,我们所能见到页面,不过多阐述;
Window:每个Activity都包含一个Window,也可以说每个Activity都包含一个Window的对象。Window是一个抽象基类,是Activity和View的交互接口,且只有一个实现类PhoneWindow。我们可以将其理解为一个载体,各种View都在这个载体上显示。

public class Activity extends ContextThemeWrappe{
   
  private Window mWindow;
}

PhoneWindow:继承于Window类,是Window的具体实现。在PhoneWindow中持有着一个很重要的View对象,DecorView。

public class PhoneWindow extends Window{
   
  private DecorView mDecor; 
}

DecorView:是所有窗口的根View,继承于FrameLayout,并对其进行扩展修饰,比如添加TitleBar等。

2、MeasureSpc:

① 在 Google 官方文档中是这么定义:

A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode.

大概意思是:MeasureSpec 封装了从父View 传递给到子View的布局需求,每个MeasureSpec代表了view的宽度或高度的测量要求,即size(大小)和mode(模式)。
② MeasureSpc是一个32位的int值,高2位代表SpecMode(测量模式),低30位代表了SpecSize(测量大小)。
③ SpecMode分为三类:
· unSpecified:父容器不对子View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。(可忽略)
· Exactly:父容器已经测量出子View的大小。对应是view的LayoutParams的match_parent或者具体数值。
· At_most:父容器指定了一个可用大小的SpecSize,view的大小不能够大于这个值,对应wrap_content。
④ 重点:View的MeasureSpec,由父 view 的MeasureSpec加上子 View 的自己的 LayoutParams,通过相应的规则转化,共同决定。
从父View的角度分析MeasureSpec的转化规则:

  • 当父View的SpecMode是Exactly的时候,即父View的大小是确定的种情况:
    ①若子View宽/高为match_parent,则子View的SpecMode也是Exactly模式,且其大小是父容器的剩余空间。
    ②若子View宽/高为wrap_parent,则子View的SpecMode为At_most模式,且其大小不超过父容器的剩余空间。
    ③若子View宽/高为具体数值,则子View的SpecMode为Exactly模式,大小就是该具体数值。
  • 当父View的SpecMode是At_most的时候,即父View的大小是不确定的情况:
    ①若子View宽/高为match_parent,则子View的SpecMode也是At_most模式,且其大小不超过父容器的剩余空间。
    ②若子View宽/高为wrap_parent,则子View的SpecMode为At_most模式,且其大小不超过父容器的剩余空间。
    ③若子View宽/高为固定宽高,则子View的SpecMode为Exactly模式,大小就是该固定宽高。

从子View的角度分析MeasureSpec的转化规则:

  • 当子View的宽/高采用固定宽高,无论父容器的SpecMode是什么,子View的SpecMode都是Exactly,其大小遵循LayoutParams的大小。
  • 当子VIew的宽/高采用match_parent:
    ①若父容器的SpecMode为Exactly,那么子View的SpecMode也为Exactly,且其大小是父容器的剩余空间。
    ②若父容器的SpecMode为At_most,那么子View的SpecMode也为At_most,且其大小是父容器的剩余空间。
  • 当子View的宽/高采用wrap_content,无论父容器是什么模式,子View的模式总是At_most,且大小不能超过父容器的剩余空间。

二、流程概述 :

view的绘制流程概括的描述为三部曲:measure -> layout -> draw

三、流程详述 :

1、ViewRootImpl (绘制入口):

Window的添加过程需要通过WindowManager的addView来实现。在addView时会创建一个ViewRootImpl对象,然后通过该对象可的setView完成Window的添加,在setView中调用requestLayout()完成异步刷新请求,在requestLayout中调用performTraversals来完场view的绘制。
总结:WindowManagerImpl -> addView -> new ViewRootImpl -> setView -> requestLayout -> performTraversals
绘制流程三部曲的核心入口就在 ViewRootImpl 类的 performTraversals() 方法中。

private void performTraversals() {
   
    ......
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    ......
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    ......
    mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
    ......
    mView.draw(canvas);
    ......
 }

2、measure(测量):

作用:measure 主要功能就是测量设置 View 的大小。
该过程由view的measure方法来完成,该方法是 final 类型,子类不能覆盖,在measure方法中会调用 onMeasure()方法,我们也可以复写 onMeasure()方法去测量设置 View 的大小。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
   
        .....
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        .....
}

onMeasure(这段代码就是measure过程的核心代码),接下来对onMeasure重点刨析:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
        setMeasuredDimension(
            getDefaultSize(getSuggestedMinimumWidth()
  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值