自定义View(8)View的绘制流程

news2024/11/11 9:34:58

安卓UI的重点之一就是View的绘制流程,经常出现在面试题中。熟悉View的绘制流程,不仅能轻松通过View相关的面试,也可以让我们更加方便的使用自定义View以及官方View。此篇先以常见面试题为切入点,说明自定义View的重要性,然后又以getMeasuredHeight值的获取作为问题点,带着问题从源码角度分析View的绘制流程。

1. 面试题介绍

1.1 Android 基础与底层机制

1. 数据库的操作类型有哪些,如何导入外部数据库?
2. 是否使用过本地广播,和全局广播有什么差别?
3. 是否使用过IntentService,作用是什么,AIDL解决了什么问题?(小米)
4. Activity、Window、View三者的差别,Fragment的特点?(360)
5. 描述一次网络请求的流程(新浪)
6. Handler、Thread和HandlerThread的差别(小米)
7. 低版本SDK实现高版本API(小米)
8. launch mode 应用场景(百度、小米、乐视)
9. touch 事件流程传递(小米)
> 10. view 绘制流程(百度)
11. 什么情况导致内存泄露(美团)
12. ANR定位和修正
13. 什么情况导致OOM (乐视、美团)
14. Android Service 与Activity 之间通信的几种方式
15. Android 各个版本API的区别
16. 如何保证一个后台服务不被杀死,比较省电的方式是什么?(百度)
17. RequestLayout、onLayout、onDraw 、DrawChild 区别与联系(猎豹)
18. Invalidate() 和 postInvalidate() 的区别及使用(百度)
19. Android 动画框架实现原理

2. 不同位置获取 getMeasuredHeight 的值

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private String TAG = "view8";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.text_view);
        Log.e(TAG, "onCreate: " + "height1 = " + mTextView.getMeasuredHeight());

        mTextView.post(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "onCreate: " + "height2 = " + mTextView.getMeasuredHeight());
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG, "onCreate: " + "height3 = " + mTextView.getMeasuredHeight());
    }
}

在这里插入图片描述
从上面代码和运行结果可知,在Activity onCreate 和 onResume 的时候都无法获取到 getMeasuredHeight 值,而使用 mTextView.post(new Runnable())方式可以获取到值,为何如此呢?

3. View 的绘制流程

3.1 View是如何被添加到屏幕窗口上

3.1.1 创建顶层布局容器DecorView

//View8/app/src/main/java/com/example/view8/MainActivity.java
// 这里主要是以默认继承的 AppCompatActivity 源码分析,如果是继承 Activity,
// 则直接进到PhoneWindow 的 setContentView,但基本流程都差不多
public class MainActivity extends AppCompatActivity {  
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);   // onCreate中主要就是操作了这一行
-------->
//.gradle/caches/modules-2/files-2.1/androidx.appcompat/appcompat/1.6.1/ace9a78b961165396147e8691faa18c1b0e48e20/appcompat-1.6.1-sources.jar!/androidx/appcompat/app/AppCompatActivity.java
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
-------->
//.gradle/caches/modules-2/files-2.1/androidx.appcompat/appcompat/1.6.1/ace9a78b961165396147e8691faa18c1b0e48e20/appcompat-1.6.1-sources.jar!/androidx/appcompat/app/AppCompatDelegate.java
    public abstract void setContentView(View v);
-------->
//.gradle/caches/modules-2/files-2.1/androidx.appcompat/appcompat/1.6.1/ace9a78b961165396147e8691faa18c1b0e48e20/appcompat-1.6.1-sources.jar!/androidx/appcompat/app/AppCompatDelegateImpl.java
    public void setContentView(View v) {
        ensureSubDecor();

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();

    private ViewGroup createSubDecor() {
        mWindow.getDecorView();  // 这里的mWindow就是PhoneWindow
-------->
//Android/Sdk/sources/android-33/com/android/internal/policy/PhoneWindow.java
    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);

    protected DecorView generateDecor(int featureId) {
        return new DecorView(context, featureId, this, getAttributes());   // 在这里new DecorView

3.1.2 在顶层布局中加载基础布局ViewGroup

//Android/Sdk/sources/android-33/com/android/internal/policy/PhoneWindow.java
    private void installDecor() {
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

    protected ViewGroup generateLayout(DecorView decor) {
        int layoutResource;
        // 通过不同的条件(主题),对 layoutResource 进行初始化,然后传入 onResourcesLoaded
            // 假设 layoutResource 走了这个,如果走了其他的,布局中也会有FrameLayout,只是上面的东西不一样
            layoutResource = R.layout.screen_simple; 
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        // ID_ANDROID_CONTENT = com.android.internal.R.id.content,也就是 layoutResource 中的 FrameLayout 布局
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
        return contentParent;  // 将 FrameLayout 布局 返回
-------->
//Android/Sdk/sources/android-33/com/android/internal/policy/DecorView.java 
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {
            // 解析layoutResource,执行了addView,添加到 mDecor 里
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-------->
<!-- Android/Sdk/platforms/android-33/data/res/layout/screen_simple.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

3.1.3 将ContentView添加到基础布局中的FrameLayout中

// 如果 MainActivity extends Activity 
//Android/Sdk/sources/android-33/com/android/internal/policy/PhoneWindow.java
    public void setContentView(int layoutResID) {
    // 将我们创建的 R.layout.activity_main 布局,放到 mContentParent 即系统创建的 FrameLayout 布局中
            mLayoutInflater.inflate(layoutResID, mContentParent); 

// 如果 MainActivity extends AppCompatActivity 
//.gradle/caches/modules-2/files-2.1/androidx.appcompat/appcompat/1.6.1/ace9a78b961165396147e8691faa18c1b0e48e20/appcompat-1.6.1-sources.jar!/androidx/appcompat/app/AppCompatDelegateImpl.java
    public void setContentView(int resId) {
        // 将我们创建的 R.layout.activity_main 布局,放到 mContentParent 即系统创建的 FrameLayout 布局中
        LayoutInflater.from(mContext).inflate(resId, contentParent);

在这里插入图片描述

3.2 View的测量

上面已经走了一遍View的添加流程,即创建DecorView,然后将我们的布局R.layout.activity_main添加进去,但是还没有走View的测量,所以还是拿不到getMeasuredHeight值的。

2. WindowManagerImpl绘制流程入口

3. 第一步Measure()源码分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1933162.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt Style Sheets-设计器集成

设计器集成 Qt Designer&#xff08;Qt Designer&#xff09;是一个出色的工具&#xff0c;用于预览样式表。您可以在 Designer 中右键单击任何小部件&#xff0c;并选择“更改样式表...”来设置样式表。 在 Qt 4.2 及更高版本中&#xff0c;Qt Designer 还包括一个样式表语法…

Unity Apple Vision Pro 开发(四):体积相机 Volume Camera

文章目录 &#x1f4d5;教程说明&#x1f4d5;教程内容概括&#x1f4d5;体积相机作用&#x1f4d5;创建体积相机&#x1f4d5;添加体积相机配置文件&#x1f4d5;体积相机配置文件参数&#x1f4d5;体积相机的边界盒大小&#x1f4d5;体积相机边界盒大小和应用边界盒大小的区别…

Redis 教程:从入门到入坑

目录 1. Redis 安装与启动1.1. 安装 Redis1.1.1. 在Linux上安装1.1.2. 在Windows上安装 1.2. 启动 Redis1.2.1. 在Linux上启动1.2.2. 在Windows上启动 1.3. 连接Redis1.3.1. 连接本地Redis1.3.2. 连接远程Redis1.3.2.1. 服务器开放端口1.3.2.2. 关闭防火墙1.3.2.3. 修改配置文件…

内网对抗-隧道技术篇防火墙组策略ICMPDNSSMB协议出网判断C2上线解决方案

知识点&#xff1a; 1、隧道技术篇-网络层-ICMP协议-判断&封装&建立&穿透 2、隧道技术篇-传输层-DNS协议-判断&封装&建立&穿透 3、隧道技术篇-表示层-SMB协议-判断&封装&建立&穿透0、不是有互联网才叫出网 1、C2常见上线采用的协议 2、常…

Android:将自定义视图设为互动式

一、简介 点击查看将自定义视图设为互动式官网文档 绘制界面只是创建自定义视图的一个部分。您还需要让视图以非常类似于您模仿的真实操作的方式响应用户输入。 让应用中的对象的行为方式与真实对象相似。例如&#xff0c;不要让应用中的图片消失后重新出现在其他位置&#x…

1.厦门面试

1.Vue的生命周期阶段 vue生命周期分为四个阶段 第一阶段&#xff08;创建阶段&#xff09;&#xff1a;beforeCreate&#xff0c;created 第二阶段&#xff08;挂载阶段&#xff09;&#xff1a;beforeMount&#xff08;render&#xff09;&#xff0c;mounted 第三阶段&#…

基于Transformer模型的谣言检测系统的实现

新书速览|PyTorch深度学习与企业级项目实战-CSDN博客 谣言检测系统项目背景 1938年10月30日的晚上&#xff0c;哥伦比亚广播公司照例安排了广播剧&#xff0c;当晚的节目是根据HG威尔斯《世界之战》改编的“火星人进攻地球”。为提升吸引力&#xff0c;制作团队选择以类纪实风…

C# 智慧大棚nmodbus4

窗体 &#xff1a;图表&#xff08;chart&#xff09;&#xff1a; 下载第三方&#xff1a; nmodbus4:可以实现串口直连&#xff0c;需要创建串口对象设置串口参数配置Serialport 如果需要把串口数据表通过tcp进行网口传递 需要创建tcpclient对象 ModbusSerialMaster master; /…

秋招突击——7/17——复习{二分查找——搜索插入位置、搜索二维矩阵,}——新作{链表——反转链表和回文链表,子串——和为K的子数组}

文章目录 引言新作二分模板二分查找——搜索插入位置复习实现 搜索二维矩阵复习实现 新作反转链表个人实现参考实现 回文链表个人实现参考实现 和为K的子数组个人实现参考实现 总结 引言 今天算法得是速通的&#xff0c;严格把控好时间&#xff0c;后面要准备去面试提前批了&a…

Camera Raw:首选项

Camera Raw 首选项 Preferences提供了丰富的配置选项&#xff0c;通过合理设置&#xff0c;可以显著提升图像处理的效率和效果。根据个人需求调整这些选项&#xff0c;有助于创建理想的工作环境和输出质量。 ◆ ◆ ◆ 打开 Camera Raw 首选项 方法一&#xff1a;在 Adobe Bri…

nginx 编译安装与配置

一、安装 官网下载合适的版本&#xff0c;建议选择稳定版本。 官网地址&#xff1a;https://nginx.org wget https://nginx.org/download/nginx-1.26.1.tar.gz -C /opt/ 解压后&#xff0c;进入源码目录 cd /opt/nginx-1.26.1tar -zxvf nginx-1.20.1.tar.gz cd nginx-1.26.1源…

react基础样式控制

行内样式 <div style{{width:500px, height:300px,background:#ccc,margin:200px auto}}>文本</div> class类名 注意&#xff1a;在react中使用class类名必须使用className 在外部src下新建index.css文件写入你的样式 .fontcolor{color:red } 在用到的页面引入…

睿考网:2024注册会计师考试考试在即,如何备考?

2024年注册会计师考试即将开始&#xff0c;准考证打印时间安排在8月5日至20日&#xff0c;每天上午8点至晚上8点&#xff0c;考生要确保在规定时间内完成准考证的打印。 注册会计师考试包含六个科目&#xff0c;每个科目都有其独特的特点和难度。考生需要根据各科目的特性采用…

vue3使用弹幕插件

1.安装插件 $ npm install vue3-danmaku --save 2.用法 <template><vue-danmaku v-model:danmus"danmus" loop style"height:100px; width:300px;"></vue-danmaku> </template><script setup> import vueDanmaku from vu…

Docker构建LNMP环境并运行Wordpress平台

1.准备Nginx 上传文件 Dockerfile FROM centos:7 as firstADD nginx-1.24.0.tar.gz /opt/ COPY CentOS-Base.repo /etc/yum.repos.d/RUN yum -y install pcre-devel zlib-devel openssl-devel gcc gcc-c make && \useradd -M -s /sbin/nologin nginx && \cd /o…

数据分析01——系统认识数据分析

1.数据分析的全貌 1.1观测 1.1.1 观察 &#xff08;1&#xff09;采集数据 a.采集数据&#xff1a;解析系统日志 当你在看视频的时候———就会产生日志———解析日志———得到数据 b.采集数据&#xff1a;埋点获取新数据&#xff08;自定义记录新的信息&#xff09; 日志…

redis之resp界面连接

解压资源 连接成功

目标检测入门:4.目标检测中的一阶段模型和两阶段模型

在前面几章里&#xff0c;都只做了目标检测中的目标定位任务&#xff0c;并未做目标分类任务。目标检测作为计算机视觉领域的核心人物之一&#xff0c;旨在从图像中识别出所有感兴趣的目标&#xff0c;并确定它们的类别和位置。现在目标检测以一阶段模型和两阶段模型为代表的。…

【spring boot】初学者项目快速练手

一小时带你从0到1实现一个SpringBoot项目开发_哔哩哔哩_bilibili 一、基础知识 1.注解 二、简介 三、项目结构 四、代码结构 1.生成Spring Boot项目的主程序 &#xff08;1&#xff09;在官网下载 Spring Initializr 快速生成一个初始的项目代码&#xff0c;会生成一个de…

Java二十三种设计模式-抽象工厂模式(3/23)

抽象工厂模式&#xff1a;复杂系统的灵活构建者 引言 在软件开发中&#xff0c;抽象工厂模式是一种提供接口以创建相关或依赖对象族的创建型设计模式。这种模式允许客户端使用一个共同的接口来创建不同的产品族&#xff0c;而无需指定具体类。 基础知识&#xff0c;java设计模…