Android中View的绘制流程

一、View绘制的起点

当建立好了decorView与ViewRoot的关联后,ViewRoot类的requestLayout()方法会被调用,以完成应用程序用户界面的初次布局。实际被调用的是ViewRootImpl类的requestLayout()方法

java 复制代码
@Override
public void requestLayout() {
  if (!mHandlingLayoutInLayoutRequest) {
    // 检查发起布局请求的线程是否为主线程
    checkThread();
    mLayoutRequested = true;
    scheduleTraversals();
  }
}

requestLayout()方法调用了scheduleTraversals()方法来调度一次完成的绘制流程,该方法会向主线程发送一个"遍历"消息,最终会导致ViewRootImpl的performTraversals()方法被调用

二、View的绘制流程

View的绘制,有三个步骤:测量(measure),布局(layout),绘制(draw), 从DecorView自上而下遍历整个View树

**注意:**是所有View执行完一个步骤后,再进行下一步,而不是一个View执行完所有步骤再遍历下一个View

  • Measure:测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

  • Layout:确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

  • Draw:绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。

    六个步骤:

    1. 绘制视图的背景

    2. 保存画布的图层(Layer)

    3. 绘制View的内容

    4. 绘制View子视图,如果没有就不用

    5. 还原图层(Layer)

    6. 绘制滚动条

三、MeasureSpec详解

MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小

Mode模式分为三类:

  • EXACTLY:对应LayoutParams中的match_parent和具体数值这两种模式。检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,

  • AT_MOST :对应LayoutParams中的wrap_content。View的大小不能大于父容器的大小。

  • UNSPECIFIED :不对View进行任何限制,要多大给多大,一般用于系统内部,如ListView,ScrollView

MeasureSpec的确定:

子View的MeasureSpec由父View根据自身的MeasureSpec和子View的LayoutParams来共同确定子View的MeasureSpec

**注意:**即使确定了子View的MeasureSpec并不一定决定了子View的大小,自定义View可以根据需要修改这个值,最终通过setMeasuredDimension(width, height)设置最终大小。

四、View执行onMeasure,onLayout的次数

根据ViewRootImpl的源码,scheduleTraversales()内部会执行postCallBack触发mTraversalRunnable重新走一遍performTraversals(),第二次执行performTraversals()就会触发performDraw()。所以performTraversals()走了两次,那么肯定会走2次measure方法

但不一定走2次onMeasure(),measure方法做了2级测量优化:

  • 1.如果flag不为forceLayout或者与上次测量规格(MeasureSpec)相比未改变,那么将不会进行重新测量(执行onMeasure方法),直接使用上次的测量值;

  • 2.如果满足非强制测量的条件,即前后二次测量规格不一致,会先根据目前测量规格生成的key索引缓存数据,索引到就无需进行重新测量;如果targetSDK小于API 20则二级测量优化无效,依旧会重新测量,不会采用缓存测量值。

五、getWidth()和getMeasuredWidth()的区别

getMeasuredWidth()、getMeasuredHeight()必须在onMeasure之后使用才有效getMeasuredWidth() 的取值最终来源于 setMeasuredDimension() 方法调用时传递的参数

getWidth()返回的是;mRight - mLeft,mRight、mLeft 变量分别表示View相对父容器的左右边缘位置,getWidth()必须在layout(int l, int t, int r, int b)执行之后才有效

六、如何在onCreate中拿到View的宽高

View.post(runnable)

java 复制代码
view.post(new Runnable() {            
            @Override
            public void run() {
                int width = view.getWidth();
                int measuredWidth = view.getMeasuredWidth();
                Log.i(TAG, "width: " + width);
                Log.i(TAG, "measuredWidth: " + measuredWidth);
            }
        });

利用Handler通信机制,发送一个Runnable到MessageQueue中,当View布局处理完成时,自动发送消息,通知UI进程。获取View的高宽属性,代码简洁

ViewTreeObserver.addOnGlobalLayoutListener()

java 复制代码
       ViewTreeObserver vto = view.getViewTreeObserver();       
       vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                Log.i(TAG, "width: " + view.getWidth());
                Log.i(TAG, "height: " + view.getHeight());
            }
        });

监听View的onLayout()绘制过程,一旦layout触发变化,立即回调onLayoutChange方法

**注意:**使用完也要主要调用removeOnGlobalListener()方法移除监听事件。避免后续每一次发生全局View变化均触发该事件,影响性能

七、invalidate和postInvalidate区别

二者都会触发刷新View,并且当这个View的可见性为VISIBLE的时候,View的onDraw()方法将会被调用

invalidate()方法在UI线程中调用,重绘当前 UI

postInvalidate()方法在非 UI 线程中调用,通过Handler通知 UI 线程重绘

相关推荐
Dovis(誓平步青云)4 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码6 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程6 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程9 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓11 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc12311 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium