自定义ViewGroup/View(2)(Measure,Layout,Draw)

2023-11-23 10:08

本文主要是介绍自定义ViewGroup/View(2)(Measure,Layout,Draw),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

> View绘制流程
View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;
View的测量大小流程:performMeasure –> measure –> onMeasure等方法;
View的测量位置流程:performLayout –> layout –> onLayout等方法;
View的绘制流程:performDraw-> draw-> onDraw等方法;

在View的测量流程里,View的测量宽高是由父控件的MeasureSpec和View自身的LayoutParams共同决定的。

-- View的绘制:测量、布局、绘制流程;
private void performTraversals() {
        final View host = mView;
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        performLayout(lp, mWidth, mHeight);
        performDraw();
    }

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                               int desiredWindowHeight) {
        final View host = mView;
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }

    private void performDraw() {
        // draw(fullRedrawNeeded) --> drawSoftware
        mView.draw(canvas);
    }
 performTraversals方法巨长,这里只截取View绘制三大流程的起点。mView就是之前缓存的DecorView。之后便开始了View的measure、layout、draw、onMeasure、onLayout、ondraw。
-- View的绘制,从ViewRoot的performTraversals()方法开始依次调用perfromMeasure、performLayout和performDraw这三个方法。这三个方法分别完成顶级View的measure、layout和draw三大流程,其中perfromMeasure会调用measure,measure又会调用onMeasure,在onMeasure方法中则会对所有子元素进行measure,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程,接着子元素会重复父容器的measure,如此反复就完成了整个View树的遍历。同理,performLayout和performDraw也分别完成perfromMeasure类似的流程。通过这三大流程,分别遍历整棵View树,就实现了Measure,Layout,Draw这一过程,View就绘制出来了。

https://img-blog.csdn.net/20181019190115563?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NoYXJlVXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
 自定义控件可能重写的方法:onMeasure(),onLayout(),onDraw()等。

-- 自定义View的分类
 1.对现有的View的子类进行扩展,例如复写onDraw方法、扩展新功能等。
 2.自定义组合控件,把常用一些控件组合起来以方便使用。
 3.直接继承View实现View的完全定制,需要完成View的测量以及绘制。
 4.自定义ViewGroup,需要复写onLayout完成子View位置的确定等工作。

-- View的工作流程主要是指measure、layout、drow这三大流程,即测量、布局和绘制,其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。
 View的整个绘制流程可以分为以下三个阶段: 
1.measure: 判断是否需要重新计算View的大小,需要则计算 ;
2.layout:判断是否需要重新计算View的位置,需要则计算 ;
3.draw:判断是否需要重新绘制View,需要则重绘;

> View控件树与ViewGroup控件容器
自定义ViewGroup:http://blog.csdn.net/lmj623565791/article/details/38339817/
自定义View与ViewGroup- https://blog.csdn.net/vfush/article/details/51613265  
https://blog.csdn.net/vfush/article/details/51790079
Android自定义View- http://blog.csdn.net/nugongahou110/article/category/5924281

View与ViewGroup:一个用户的交互Android设备可能的例外是主要的视觉和触觉性质。
setContentView之后才为布局里的元素分配内存,在未分配内存前对元素进行访问时没有意义的!!!
WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。
-- 一般来说,开发Android应用程序的UI界面都不会直接使用View和ViewGroup,而是使用这两大基类的派生类。

 - View
   View派生出的直接子类有:AnalogClock,ImageView,KeyboardView, ProgressBar,SurfaceView,TextView,ViewGroup,ViewStub
 View派生出的间接子类有:AbsListView,AbsSeekBar, AbsSpinner, AbsoluteLayout, AdapterView<T extends Adapter>,AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, AutoCompleteTextView,Button,CalendarView, CheckBox, CheckedTextView, Chronometer, CompoundButton,

 - ViewGroup
 ViewGroup派生出的直接子类有:AbsoluteLayout,AdapterView<T extends Adapter>,FragmentBreadCrumbs,FrameLayout,LinearLayout,RelativeLayout,SlidingDrawer
    ViewGroup派生出的间接子类有:AbsListView,AbsSpinner, AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, CalendarView, DatePicker, DialerFilter, ExpandableListView, Gallery, GestureOverlayView,GridView,HorizontalScrollView, ImageSwitcher,ListView,

  上述的所有基类、派生类都是Android framework层集成的标准系统类,开发者在应用开发中可直接引用SDK中这些系统类及其API。但事实上,在UI开发的很多场景下,直接使用这些系统类并不能满足应用开发的需要。比如说,我们想用ImageView在默认情况下加载一幅图片,但是希望在点击该View时View变换出各种图像处理效果,这个时候直接使用ImageView是不行的,此时我们可以重载ImageView,在新派生出的子控件中重载OnDraw等方法来实现我们的定制效果。这种派生出系统类的子类方法我们通常称之为自定义控件。
  protected void onDraw(Canvas canvas):View类中用于重绘的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,也是Android UI绘制最重要的方法。开发者可重载该方法,并在重载的方法内部基于参数canvas绘制自己的各种图形、图像效果。
  protected void onLayout(boolean changed, int left, int top, int right, int bottom):View类中布局发生改变时会调用的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,重载该类可以在布局发生改变时作定制处理,这在实现一些特效时非常有用。
  protected void dispatchDraw(Canvas canvas):ViewGroup类及其派生类具有的方法,这个方法主要用于控制子View的绘制分发,重载该方法可改变子View的绘制,进而实现一些复杂的视效,典型的例子可参见Launcher模块Workspace的dispatchDraw重载。
  protected boolean drawChild(Canvas canvas, View child, long drawingTime)):ViewGroup类及其派生类具有的方法,这个方法直接控制绘制某局具体的子view,重载该方法可控制具体某个具体子View。
 
 ViewGroup是View的子类,所以它也具有View的特性,但它主要用来充当View的容器,将其中的View视作自己的孩子,对它的子View进行管理,当然它的孩子也可以是ViewGroup类型。
 ViewGroup(树根)和它的孩子们(View和ViewGroup)以树形结构形成了一个层次结构,View类有接受和处理消息的功能,android系统所产生的消息会在这些ViewGroup和 View之间传递。

> 绘制流程:
  绘制按照视图树的顺序执行。视图绘制时会先绘制子控件。如果视图的背景可见,视图会在调用onDraw函数之前绘制背景。强制重绘,可以使用invalidate()。
 -- 事件的基本流程如下:
  1、事件分配给相应视图,视图处理它,并通知相关监听器。
  2、操作过程中如果发生视图的尺寸变化,则该视图用调用requestLayout()方法,向父控件请求再次布局。
  3、操作过程中如果发生视图的外观变化,则该视图用调用invalidate()方法,请求重绘。
  4、如果requestLayout()或invalidate()有一个被调用,框架会对视图树进行相关的测量、布局和绘制。
  注意,视图树是单线程操作,直接调用其它视图的方法必须要在UI线程里。跨线程的操作必须使用句柄Handler。

> Android实现的是C/S模式:Activity的创建
【1】ActivityManagerService创建Activity线程,激活一个activity
【2】系统调用Instrumentation.newActivity创建一个activity
【3】Activity创建后,attach到一个新创建的phonewindow中。这样Activity获取一个唯一的WindowManager服务的实例
【4】Activity创建过程中使用setContentView设置用用户UI,这些view被加入到PhoneWindow的ContentParent中。
【5】Activity线程继续执行,当执行到Activity.makeVisible是将根view DecoView加入到WindowManger中,WindowManger实全会为每个DecoView创建对应的ViewRoot
【6】每个ViewRoot拥有一个Surface,每个Surface将会调用底层库创建图形绘制的内存空间。这个底层库就是SurfaceFlinger。SurfaceFlinger同时也负责将个View绘制的图形合到一块(按照Z轴)显示到用户屏幕。
【7】如果用户直接在Canvas上绘制,实际上它直接操作Surface。但对每个View的变更,它是要通知到ViewRoot,然后 ViewRoot获取Canvas。如果绘制完成,surfaceFlinger得到通知,合并Surface成一个Surface到设备屏幕。

 真正显示图形的实际上跟Activity没有关系,完全由WindowManager来决定。 WindowManager是一个系统服务,因此可以直接调用这个服务来创建界面,并且更绝的是Dialog、Menu也是有WindowManager 来管理的。另外一个我们也可以看到,最底层都是Surface来,因此,常见开发游戏的人都推荐你使用SurfaceView来创建界面。
 

这篇关于自定义ViewGroup/View(2)(Measure,Layout,Draw)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/417166

相关文章

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并