View 自定义 - 绘制前的准备 DecorView

news2024/11/20 1:28:28

一、概念

图中可以看出 ViewRoot 最后一步是绘制,在绘制之前系统会有一些准备,即前面几个步骤:创建PhoneWindow、DecorView、ViewRootmpl。

二、DecorView 的创建

DecorView的创建开始是从 Activity 中 setContentView() 开始的。

  • 创建抽象类 Window 的子类 PhoneWindow 实例。
  • 为 PhoneWindow 对象设置 WindowManager 对象。
  • 为 PhoneWindow 对象创建一个 DecroView 对象(根据所选的主题样式增加)。
  • 为 DecroView 对象中的 content 增加 Activity 中设置的布局文件。

此时,DecorView(即顶层View)已创建和添加Activity中设置的布局文件中,但目前仍未显示出来,即不可见。

/**
  * 具体使用:Activity的setContentView()
  */
  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

/**
  * 源码分析:Activity的setContentView()
  */
   public void setContentView(int layoutResID) {
        // getWindow() 作用:获得Activity 的成员变量mWindow ->>分析1
        // Window类实例的setContentView() ->>分析2
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
   }

/**
  * 分析1:成员变量mWindow
  */
  // 1. 创建一个Window对象(即 PhoneWindow实例)
  // Window类 = 抽象类,其唯一实现类 = PhoneWindow
  mWindow = new PhoneWindow(this, window);
  
  // 2. 设置回调,向Activity分发点击或状态改变等事件
  mWindow.setWindowControllerCallback(this);
  mWindow.setCallback(this);

  // 3. 为Window实例对象设置WindowManager对象
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    }

/**
  * 分析2:Window类实例的setContentView()
  */
  public void setContentView(int layoutResID) {
        // 1. 若mContentParent为空,创建一个DecroView
        // mContentParent即为内容栏(content)对应的DecorView = FrameLayout子类
        if (mContentParent == null) {
            installDecor(); // ->>分析3
        } else {
            // 若不为空,则删除其中的View
            mContentParent.removeAllViews();
        }
        // 2. 为mContentParent添加子View
        // 即Activity中设置的布局文件
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();//回调通知,内容改变
        }
    }

/**
  * 分析3:installDecor()
  * 作用:创建一个DecroView
  */
  private void installDecor() {
    if (mDecor == null) {
        // 1. 生成DecorView ->>分析4
        mDecor = generateDecor(); 
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    // 2. 为DecorView设置布局格式 & 返回mContentParent ->>分析5
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor); 
        ...
        } 
    }
}

/**
  * 分析4:generateDecor()
  * 作用:生成DecorView
  */
  protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
    // 回到分析原处

/**
  * 分析5:generateLayout(mDecor)
  * 作用:为DecorView设置布局格式
  */
  protected ViewGroup generateLayout(DecorView decor) {
        // 1. 从主题文件中获取样式信息
        TypedArray a = getWindowStyle();
        // 2. 根据主题样式,加载窗口布局
        int layoutResource;
        int features = getLocalFeatures();
        // 3. 加载layoutResource
        View in = mLayoutInflater.inflate(layoutResource, null);
        // 4. 往DecorView中添加子View
        // 即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 
        mContentRoot = (ViewGroup) in;
        // 5. 这里获取的是mContentParent = 即为内容栏(content)对应的DecorView = FrameLayout子类
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 
        return contentParent;
    }

三、DecorView 的显示

在主线程创建时,会调用handleResumeActivity(),DecorView的显示操作从此处开始。

  • 将 DecorView 对象添加到 WindowManager 中。
  • 创建 ViewRootImpl 对象。
  • WindowManager 将 DecorView 对象交给 ViewRootImpl 对象。
  • ViewRootImpl 对象通过 Handler 向主线程发送了一条触发遍历操作的消息:performTraversals(),该方法用于执行 View 的绘制流程(measure、layout、draw)。 

ViewRootImpl 对象中接收的各种变化(如来自WmS的窗口属性变化、来自控件树的尺寸变化、重绘请求等都引发 performTraversals() 的调用及完成相关处理,并最终显示到可见的Activity中。

/**
  * 源码分析:主线程创建时,调用的handleResumeActivity()
  */
  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
/**
  * 源码分析:Activity的setContentView()
  */
 final void handleResumeActivity(IBinder token,
    boolean clearHide, boolean isForward, boolean reallyResume) {
    ActivityClientRecord r = performResumeActivity(token, clearHide);
    if (r != null) {
        final Activity a = r.activity;
          if (r.window == null && !a.mFinished && willBeVisible) {
        // 1. 获取Window实例中的Decor对象
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        // 2. DecorView对用户不可见
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        // 3. DecorView被添加进WindowManager了
        // 此时,还是不可见
        if (a.mVisibleFromClient) {
            a.mWindowAdded = true;
            wm.addView(decor, l);
        }
        // 4. 此处设置DecorView对用户可见
        if (!r.activity.mFinished && willBeVisible
            && r.activity.mDecor != null && !r.hideForNow) {
             if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                    // —>>分析1
                }
            }
    }
/**
  * 分析1:Activity.makeVisible()
  */
  void makeVisible() {
   if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            // 1. 将DecorView添加到WindowManager ->>分析2
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        // 2. DecorView可见
        mDecor.setVisibility(View.VISIBLE);
    }

/**
  * 分析2:wm.addView
  * 作用:WindowManager = 1个接口,由WindowManagerImpl类实现
  */
  public final class WindowManagerImpl implements WindowManager {    
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow); ->>分析3
    }
}

/**
  * 分析3:WindowManagerGlobal 的addView()
  */
  public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {

     final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
     ...
     synchronized (mLock) {
     // 1. 实例化一个ViewRootImpl对象
     ViewRootImpl root;
     root = new ViewRootImpl(view.getContext(), display);
     view.setLayoutParams(wparams);
     mViews.add(view);
     mRoots.add(root);
     mParams.add(wparams);
     }
     // 2. WindowManager将DecorView实例对象交给ViewRootImpl 绘制View
     root.setView(view, wparams, panelParentView);
     // ->> 分析4
       }
    }
 }

/**
  * 分析4:ViewRootImpl.setView()
  */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        requestLayout(); // ->>分析5
    }

/**
  * 分析5:ViewRootImpl.requestLayout()
  */
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            // 1. 检查是否在主线程
            checkThread();
            mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
            // 2. ->>分析6
            scheduleTraversals();
        }
    }

/**
  * 分析6:ViewRootImpl.scheduleTraversals()
  */
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 通过mHandler.post()发送一个runnable,在run()方法中去处理绘制流程
            // 与ActivityThread的Handler消息传递机制相似
            // ->>分析7
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

/**
  * 分析7:Runnable类的子类对象mTraversalRunnable
  * 作用:在run()方法中去处理绘制流程
  */
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal(); // ->>分析8
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

/**
  * 分析8:doTraversal()
  */
    void doTraversal() {
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals(); 
            // 最终会调用performTraversals(),从而开始View绘制的3大流程:Measure、Layout、Draw
    }

// 注:
// a. ViewRootImpl中W类是Binder的Native端,用于接收WmS处理操作
// b. 因W类的接收方法是在线程池中的,故可通过Handler将事件处理切换到主线程中

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

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

相关文章

项目管理的10个经典法则

大家好,我是老原。 前两天给大家更新了一篇实用文,分享了6张能搞定项目管理的思维导图,很多粉丝朋反馈对项目更有思路了。 那6张思维导图都需要建立在一定的项目管理法则上,今天我也给你们整了项目管理的10个经典法则。 建议大…

域控主机 带瘤

1.装环境 是dns环境 加入域 二.文件上传 2.cs木马生成 服务器 75.233 上传木马成功 上线

C语言之通讯录的实现篇优化版

目录 动态内存管理 通讯录声明 静态版本 动态版本 ​初始化通讯录 静态版本 动态版本 Add增加通讯录 静态版本 动态版本 Checkcapacity增容 DestroyContact释放动态空间 文件操作 SaveContact保存信息到文件中 初始化通讯录 旧版本 文件版本 LoadContact加载…

Hudi-源码-索引-bloom 索引

文章目录 前言问题原理TagLocation流程入口LookupIndexfindMatchingFilesForRecordKeysHoodieKeyLookupHandle 如何优化问题一 如何避免大量 IO问题二 如何减少计算 Hash问题三 使用什么结构优化比对结果如何初始化树查询 总结 前言 Hudi 系列文章在这个这里查看 https://gith…

图论与网络优化

2.概念与计算 2.1 图的定义 2.1.1 定义 图(graph) G G G 是一个有序的三元组&#xff0c;记作 G < V ( G ) , E ( G ) , ψ ( G ) > G<V(G),E(G),\psi (G)> G<V(G),E(G),ψ(G)>。 V ( G ) V(G) V(G) 是顶点集。 E ( G ) E(G) E(G) 是边集。 ψ ( G ) \…

【合集】Redis——Redis的入门到进阶 结合实际场景的Redis的应用

前言 Redis是一个开源的内存数据结构存储系统&#xff0c;也被称为键值存储系统。它支持多种数据结构&#xff0c;如字符串、哈希表、列表、集合、有序集合等&#xff0c;并提供了丰富的操作命令&#xff0c;可以对这些数据结构进行快速的读写操作。Redis具有高性能、高可用性…

驱动:驱动相关概念,内核模块编程,内核消息打印printk函数的使用

一、驱动相关概念 1.操作系统的功能 向下管理硬件&#xff0c;向上提供接口 操作系统向上提供的接口类型&#xff1a; 内存管理&#xff1a;内存申请&#xff08;malloc&#xff09; 内存释放&#xff08;free&#xff09;等 文件管理&#xff1a; 通过文件系统格式对文件ext2…

this指向详解

目录 一&#xff1a;严格模式与非严格模式 1.严格模式的开启 2.this指向的一些情况&#xff1a; 二&#xff1a;如何指定this的值&#xff1f; 1.在调用时指定this的值 2.在创建时指定this的值 ​编辑三&#xff1a; 结尾 一&#xff1a;严格模式与非严格模式 在非严格模…

项目管理之分析项目特点的方法

在管理项目时&#xff0c;了解项目的目标和实现方法可以帮助我们更好地规划和执行项目。根据项目的目标和实现方法的不同&#xff0c;可以将项目分为四种类型&#xff1a;地、水、火和气。 对于工程项目&#xff0c;采用基于活动任务的计划管理方法&#xff0c;使用活动网络图…

聊聊分布式架构08——SpringBoot开启微服务时代

目录 微服务架构时代 快速入门 入门详解 SpringBoot的自动配置 石器时代&#xff1a;XML配置bean 青铜时代&#xff1a;SpringConfig 铁器时代&#xff1a;AutoConfigurationImportSelector 手写简单Starter SpringApplication启动原理 微服务架构时代 Spring Boot的…

LabVIEW中将枚举与条件结构一起使用

LabVIEW中将枚举与条件结构一起使用 枚举是一个具有相应数值的字符串标签型列表。在LabVIEW&#xff08;U8 &#xff0c; U16-默认值和U32&#xff09;中以无符号整数形式应用。 例如&#xff0c;可以有一个枚举保存四个季节&#xff0c;在这种情况下&#xff0c;每个字符串都…

2022最新版-李宏毅机器学习深度学习课程-P26RNN-2

一、RNN网络结构 与时间有关的反向传播&#xff08;每次不同&#xff09; 损失函数 实验其实不容易跑&#xff0c;因为他的损失函数曲线幅度很大 画出来差不多是这个样子。突然一下升高是因为从右到左碰到陡峭的地方梯度一下变大了&#xff0c;所以弹回去了。 原作者在训练时…

JAVA反射(原理+使用)

引言 反射是一种机制&#xff0c;能够使java程序在运行过程中&#xff0c;检查&#xff0c;获取类的基本信息&#xff08;包&#xff0c;属性&#xff0c;方法等&#xff09;&#xff0c;并且可以操作对象的属性和方法 反射是框架实现的基础 反射的原理 讲述反射的原理之前&a…

covfefe 靶机/缓冲区溢出

covfefe 信息搜集 存活检测 详细扫描 后台网页扫描 80 端口 31337 端口 网页信息搜集 分别访问扫描出的网页 说有三个不允许看的内容 尝试访问 第一个 flag 访问 .ssh 文件 继续根据提示访问 获取了三个 ssh 文件 ssh 登录 在下载的 id_rsa_pub 公钥文件中发现了…

leetCode 11. 盛最多水的容器 + 双指针

11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/container-with-most-water/description/?envTypestudy-plan-v2&envIdtop-interview-150 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是…

【Java基础面试三十三】、接口和抽象类有什么区别?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;接口和抽象类有什么区别…

大同小异!如何在苹果不同类型设备上更改AirDrop的名称

你可以更改你的AirDrop ID&#xff0c;让其他人看到你名字之外的东西。本文介绍了如何在iPhone、iPad和Mac上更改AirDrop名称。 如何在iPhone上更改AirDrop名称 在iPhone上更改AirDrop名称涉及到你可能不想做的更改。幸运的是&#xff0c;这在iPad和Mac上不是真的&#xff0c…

【408数据结构】第一章 绪论

第一章 绪论 1.数据结构基本概念及三要素 一.数据结构基本概念 1.数据 信息的载体&#xff0c;能被客观事物描述的数字&#xff0c;字符以及能被计算机程序识别和处理的符号的集合 2.数据元素 数据的基本单位&#xff0c;一个数据元素可由若干个数据项&#xff08;构成数…

Unity Animation--动画剪辑(创建动画)

创建一个新的动画编辑 创建新的动画剪辑 &#xff0c;在场景中选择一个GameObject&#xff0c;然后打开“ 动画”窗口&#xff08;顶部菜单&#xff1a;&#xff09;“ 窗口” >“ 动画” >“ 动画”。 如果GameObject 中尚未分配任何动画剪辑&#xff0c;“创建”按钮…

pytorch nn.Embedding 读取gensim训练好的词/字向量(有例子)

最近在跑深度学习模型&#xff0c;发现Embedding随机性太强导致模型结果有出入&#xff0c;因此考虑固定初始随机向量&#xff0c;既提前训练好词/字向量&#xff0c;不多说上代码&#xff01;&#xff01; 1、利用gensim训练字向量&#xff08;词向量自行修改&#xff09; #…