基础巩固(六)自定义View

文章目录

  • View绘制流程
    • Measure
    • Layout
    • Draw
  • 自定义View的实现的步骤
    • 步骤1:实现Measure、Layout、Draw流程
      • 自定义 Measure
        • ViewGroup.LayoutParams
        • MeasureSpec
      • 自定义Layout
    • 自定义属性
  • 绘制工具类
    • Paint
      • 具体使用
    • Path
    • Canvas

View绘制流程

基础巩固(六)自定义View_第1张图片
在绘制前,系统会有一些绘制准备,创建PhoneWindow,DecorView,ViewRootImpl。这个过程在Activity的onCreate()。

View的绘制流程开始于 ViewRootImpl对象的performTraversals()

/**
  * 源码分析:ViewRootImpl.performTraversals()
  */
  private void performTraversals() {
  		// 1. 执行measure流程
        // 内部会调用performMeasure()
        measureHierarchy(host, lp, res,desiredWindowWidth, desiredWindowHeight);

        // 2. 执行layout流程
        performLayout(lp, mWidth, mHeight);

        // 3. 执行draw流程
        performDraw();
    }

View绘制流程从顶级View(DecorView)的ViewGroup开始,一层一层从ViewGroup至子View遍历测绘:measure、layout、draw。
基础巩固(六)自定义View_第2张图片

  • 从根视图开始,深度优先搜索遍历每个子view执行measure

  • 从根视图开始,深度优先搜索遍历每个子view执行layout

  • 从根视图开始,深度优先搜索遍历每个子view执行draw

基础巩固(六)自定义View_第3张图片

Measure

measure的作用是测量View的宽 / 高
在一些情况下,视图的尺寸受到动态内容、响应式设计、布局约束或动画效果的影响时,可能需要进行多次测量才能确定最终的宽度或高度,因此measure过程得到的 宽 / 高可能不准确,建议在layout过程中的onLayout去获取最终的宽 / 高。

单一view的measure过程:

  • measure():基本测量逻辑的判断
  • onMeasure():分为两步
    • getDefaultSize():根据View宽 / 高测量规格计算View的宽高值
    • setMeasure():存储测量后的子view宽 / 高

ViewGroup的measure流程:

注意:ViewGroup的measure过程中,等待所有的子view都measure完毕,合并所有子view的尺寸才可以得到ViewGroup父视图的测量值。

基础巩固(六)自定义View_第4张图片

Layout

Layout过程的作用是计算视图View的位置,即
View的四个顶点位置:Left、Top、Right 和 Bottom。
基础巩固(六)自定义View_第5张图片

Draw

Draw过程的作用是绘制View视图
单一View只需要绘制自身(含背景、内容)和装饰。

注意:这里的装饰指的是指示器、滚动条和前景

ViewGroup的绘制包括:

  • 自身绘制(含背景和内容)
  • 遍历子View,逐个绘制(背景和内容及装饰)
  • ViewGroup的绘制装饰
    基础巩固(六)自定义View_第6张图片

自定义View的实现的步骤

步骤1:实现Measure、Layout、Draw流程

  • 从View的工作流程(measure过程、layout过程、draw过程)来看,若要实现自定义View,根据自定义View的种类不同(单一View / ViewGroup),需自定义实现不同的方法
  • 主要是onMeasure()、onLayout()、onDraw(),具体如下:
    基础巩固(六)自定义View_第7张图片

自定义 Measure

ViewGroup.LayoutParams

ViewGroup 的子类(RelativeLayout、LinearLayout)有其对应的 ViewGroup.LayoutParams 子类

它的作用是指定视图View的高度和宽度等布局参数。
具体通过以下参数指定:
基础巩固(六)自定义View_第8张图片
对应xml文件中:

android:layout_height="wrap_content"   //自适应大小  
android:layout_height="match_parent"   //与父视图等高  
android:layout_height="fill_parent"    //与父视图等高  
android:layout_height="100dip"         //精确设置高度值为 100dip  

MeasureSpec

MeasureSpec:策略规则,是由测量模式(mode)和测量大小(size)组成,共32位(int类型),其中mode占2位,大小占低30位。

基础巩固(六)自定义View_第9张图片
测量模式分为三种:

  • UNSPECIFIED:父视图不约束子View,例如ListView、ScrollView,不过一般自定义View不用这个。
  • EXACTLY:父视图为子视图指定一个确切的尺寸,子视图大小必须是这个尺寸大小 。例如match_parent,或者指定具体的数值100dp,这种模式下,视图的测量过程会忽略视图的布局参数(LayoutParams
  • AT_MOST:父视图为子视图指定一个最大尺寸,子视图必须确保自身与所有子视图可以适应在该尺寸内。例如wrap_content,自适应大小,具体尺寸根据需求设定。这种模式下父控件无法确定子View大小,只能根据子控件自身需要设计计算尺寸。

MeasureSpec的具体使用:
MeasureSpec类用一个变量封装了测量模式(mode)和测量大小(size):通过使用二进制,将测量模式(mode)和测量大小(size)打包成一个int值,并提供了打包和解包的方法,这样的做法是为了减少对象内存分配和提高存取效率。具体使用如下所示:

// 1. 获取测量模式(Mode)
int specMode = MeasureSpec.getMode(measureSpec)

// 2. 获取测量大小(Size)
int specSize = MeasureSpec.getSize(measureSpec)

// 3. 通过Mode 和 Size 生成新的SpecMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);

自定义Layout

Layout的作用是计算View的四个顶点位置:Left、Top、Right 和 Bottom

自定义属性

  1. 在values目录下创建自定义属性的xml文件
  2. 在自定义View的构造方法中加载自定义XML文件 & 解析属性值
  3. 在布局文件中使用自定义属性

绘制工具类

Paint

Paint:画笔,它的作用是确定绘制内容的具体效果(如颜色、大小等等),在绘制内容时需要画笔。

具体使用

步骤:

  1. 创建一个Paint对象
  2. 设置画笔,即绘制内容时的效果,如颜色、大小
  3. 初始化画笔(尽量选择在View的构造函数)

Path

Canvas

Canvas:画布,是一种绘制时规则,绘制内容是根据画布的规定绘制在屏幕上的。

你可能感兴趣的:(Android,java,android,开发语言)