View的绘制流程

View的绘制流程

View 和 ViewGroup

View的继承树:
View的继承树
从View的继承树中可以看出,常见的TextView、ImageView均是继承于View的。
还有一个比较重要的ViewGroup也是继承于View的。
ViewGroup的继承树:
ViewGroup的继承树
我们平时使用的一些布局就是ViewGroup的(用于包含其他的View),ViewGroup本身就是View。

View的绘制流程

一般我们的页面都是通过Activity中的onCreate()方法中的setContentView()设置进去的。
在这里插入图片描述
查看setContentView()
还需要继续去找对应的实现。我的版本为Android 10。
AppCompatDelegate类中的setContentView()是一个抽象方法。在这里插入图片描述
通过查看对应的实现类即:AppCompatDelegateImpl 类,进入该类搜索setContentView方法。
在这里插入图片描述

 @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        //找到ID为content的ViewGroup,它是一个Activity默认就有的布局。
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        //清空contentParent内部的所有的子View
        contentParent.removeAllViews();
        //解析resId对应的布局文件,将他嵌入到contentParent内部
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        //这里在发生内容变化的时候,通知某个类。这里发生了一个回调(Widow Callback)。
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

setContentView()只是根据布局文件创建对应控件的View对象,并没有涉及怎么去绘制一个View。

View的绘制流程包括(View类中的三个API):measure(测量)、layout(布局)、draw(绘制)。

  1. 测量(measure) 在这里插入图片描述
    这方法里面会去调用onMeasure()方法。在onMeasure方法中有一个setMeasuredDimension(),他必须被调用,否则会抛出IllegalStateException的异常。这个方法内部完成了对成员变量mMeasuredWidth和mMeasuredHeight的赋值。如果没有调用默认为零。

MeasureSpec中的前两位为父容器对子View的限制模式,后30位表示大小值。模式在MeasureSpec类中定义为:

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
/**
* Measure specification mode: The parent has not imposed any constraint
* on the child. It can be whatever size it wants.
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;

/**
* Measure specification mode: The parent has determined an exact size
* for the child. The child is going to be given those bounds regardless
* of how big it wants to be.
*/
public static final int EXACTLY     = 1 << MODE_SHIFT;

/**
* Measure specification mode: The child can be as large as it wants up
* to the specified size.
*/
public static final int AT_MOST     = 2 << MODE_SHIFT;

  1. 布局(layout)

在这里插入图片描述
参数的描述:
l Left position, relative to parent
t Top position, relative to parent
r Right position, relative to parent
b Bottom position, relative to parent

  1. 绘制(draw)

在这里插入图片描述
绘制过程的六步。

  1. Draw the background
  2. If necessary, save the canvas’ layers to prepare for fading
  3. Draw view’s content
  4. Draw children
  5. If necessary, draw the fading edges and restore layers
  6. Draw decorations (scrollbars for instance)
  7. If necessary, draw the default focus highlight
    在这里插入图片描述

ViewGroup的绘制流程

ViewGroup 是一个 View的容器,可以包含子View,作为一个父容器,他应该承担更多的职责。
根据VIew的绘制流程的内容,一个View的测量、布局和绘制都是由父容器调用。如果以继承ViewGroup的方式自定义一个控件或布局,就必须要考虑是否以及如何测量布局和绘制子View。

ViewGroup的测量

这个我们可以去选择看DecorView的实现,或者去看线性布局等。DecorView的本质是一个FrameLayout。
VIewGroup的测量过程遵循View的测量过程,只是它还要发起子View的测量,通常测量子View在onMeasure方法中执行,一般的过程是遍历ViewGroup中的所有子View,获取子View在布局中的参数LayoutParams的宽高,然后指定一个模式,将宽高和模式构建成对子View的宽高的限制widthMeasureSpec和heightMeasureSpec ,然后调用子View的measure方法传入限制,完成对子View的测量。

ViewGroup的布局

ViewGroup的布局过程遵循View的布局过程,只是它还要发起子View的布局。ViewGroup同样会调用layout方法来完成自身的布局,并且在layout方法里还会调用onLayout方法。根据View类中layout方法的注释,如果View的子类是一个ViewGroup时,他必须实现onLayout方法,在onLayout方法中调用它包含的子View的layout方法去布局子View。
在这里插入图片描述

ViewGroup的绘制

在View类的绘制方法draw()中,第三步是调用onDraw方法绘制view的内容,第四步是调用dispatchDraw方法绘制子View,如果一个View没有子View,也就是不是VIewGroup,只需要实现onDraw方法绘制自己就可以了,如果是ViewGroup,则需要实现dispatchDraw()方法去绘制子View。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

View的绘制流程深入解析

通过查看布局树,我们可以知道最顶层的视图是DecorView。
App的入口是ActivityThread类,他管理Activity的生命周期,其中包括了onResume方法的调用。
ActivityThread类执行handleResumeActivity方法,在handleResumeActivity 方法中会触发 onResume 方法的调用,接着后续代码会创建出 RootViewImpl 类来对窗口管理器 WindowManagerService 中添加PhoneView,在添加之前就会调用 RootViewImpl 内部的 requestLayout 方法来布局整个Activity的视图树。

  • ActivityThread中的handleResumeActivity 方法

在这里插入图片描述

  • RootViewImpl 内部的 requestLayout 方法

在这里插入图片描述

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();//检查是否在UI线程
            mLayoutRequested = true;//标记当前正在请求的UI布局
            scheduleTraversals();// 开启布局
        }
    }

scheduleTraversals 内部会执行一个 mTraversalRunnable 。
在这里插入图片描述
mTraversalRunnable 是一个Runnable对象,他的 run方法中调用了doTraversal()方法。
在这里插入图片描述
在这里插入图片描述

performTraversals 就是用来遍历视图树的。
这里面有

// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//开始测量

performLayout(lp, mWidth, mHeight);//开始布局过程

performDraw(); //开启绘制过程。

选择其中一个方法,会发现其中会调用View的相应方法。
在这里插入图片描述

参照自Android应用开发进阶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值