View的绘制过程分析

写在前面:
最近在学习《Android开发艺术探索》关于View的工作原理的内容,虽然讲解的很细致,但总觉得仅仅看一遍还是难以对View的整体绘制过程有较为明晰的认识。接下来,我根据简单的代码,重点观察分析View的measure– layout – draw 绘制过程。

一、xml文件

其中,RevealLayout本质上就是LinearLayout。MyButton本质上就是Button。这里是为了方便观察绘制过程,对相关方法添加了打印信息。

<com.ryg.chapter_4.ui.RevealLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="12dp"
    tools:context="${relativePackage}.${activityClass}" >

    <com.ryg.chapter_4.ui.MyButton
        android:id="@+id/button1"
        style="@style/AppTheme.Button.Green"
        android:onClick="onButtonClick"
        android:text="View Test" />

</com.ryg.chapter_4.ui.RevealLayout>

二、 运行结果

如下图,就只显示一个简单的Button,重点是对其绘制过程的观察。
这里写图片描述

三、View绘制过程方法调用

控件构造器:RevealLayout()、MyButton() → RevealLayout().onMeasure→ RevealLayout().measureChildWithMargins→ MyButton.onMeasure → RevealLayout().onLayout → MyButton.layout → MyButton.onLayout → RevealLayout().draw → RevealLayout().onDraw → RevealLayout().dispatchDraw → MyButton.draw → MyButton.onDraw
这里写图片描述

四、View绘制过程分析

1) View的绘制过程从何处开始呢?

关于这个问题,可参考http://www.cnblogs.com/jerehedu/p/4679534.html。Acitvity启动过程中,追溯其源码,可得出下图所示的调用过程:
这里写图片描述

Activity在启动过程中会调用主线程ActivityThread的handleResumeActivity方法,此方法会将DecorView和WindowManagerImpl对象关联起来。最终,ActivityThread中生成的DecorView经过WindowManagerImpl、WindowManagerGlobal,最终调用了ViewRootImpl中的setView方法,将DecorView设置赋值给了ViewRootImpl中的mView属性。在ViewRootImpl中调用的performTraversals方法将会开启View的整个绘制过程。

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        ……
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ……
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ……
        performDraw();
        ……
}

2)View绘制过程深入
这里写图片描述
measure过程:
1.View的measure方法为final类型,不能被重写。在measure方法中会调用onMeasure方法,因而一般重写onMeasure方法实现自定义View。
2.View的onMeasure方法本身设定View的测量后大小为android:minWidth属性所设置的值,此属性默认为0。如果为View指定了背景,则View的测量后大小为背景图片的原始尺寸。另外,直接继承View的控件需要重写onMeasure方法设置wrap_content时的自身大小,否则其效果等同于match_parent.
3.对于ViewGroup实例,比如LinearLayout、RelativeLayout等,不仅要根据自己的布局特性重写onMeasure方法还要实现对子元素进行measure的方法。
4.view的大小取决于:父容器的MeasureSpec、父容器的padding以及view本身的LayoutParams。
5.measure过程可能要进行多次才能确定最终的测量宽/高,因而在measure过程中去获取view的宽/高可能会不准建议在onLayout方法中获取。
6.view的measure过程和Activity的生命周期方法并不同步。不能保证某一个生命周期阶段measure确定完成。

layout过程:
1.layout方法确定View本身的位置,onLayout方法确定所有子元素的位置。
2.onLayout的具体实现和具体布局有关,所以View和ViewGroup都只提供了空方法,需要子类重写。
3.对ViewGroup实例,在onLayout方法中会遍历子元素调用其layout方法。
4.在onLayout方法中需要对LayoutParameters.margin进行处理。

draw过程:
View.draw()分为以下几步:
(1)background.draw():绘制背景;
(2)onDraw():绘制自己;
(3)dispatchDraw():绘制子元素;
(4)onDrawScrollbars:绘制装饰。
1.自定义View一般会重写onDraw方法,需要处理padding。
2.dispatchDraw方法实现draw事件的一层层传递。
3.自定义View时序注意View中的setWillNotDraw方法。此方法为优化标志位。
setWillNotDraw(true)表示view不需要绘制任何内容,系统会进行响应优化。
默认情况下,View未启动此标志位,ViewGroup启动此标志位。
当自定义控件继承自ViewGroup时且需要绘制内容时需显式关闭此优化标记位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值