自定义View学习

  • 自定义View是Android中的一个重要部分,设计需要对UI界面进行定制化时,往往会用到自定义View的相关知识。

学习来源:Carson带你学自定义View

自定义View的类型

1、自定义组合控件:多个控件组合成一个新的控件,方便复用,例如项目中使用的自定义Dialog。
2、继承系统子View:继承系统的原生View再进行扩展,例如项目中的NumKeyView。
3、继承系统ViewGroup:继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展。

自定义View的基础概念:

1、Android中的坐标系:

在Android坐标系中,以屏幕的左上角为原点,原点往右为正,往下为正。如下图:
在这里插入图片描述

2、View坐标系:

在View当中,其位置都是使用与父View的位置来进行确定的,如下图:

在这里插入图片描述

通过与父View的相对位置来进行确定子View的位置。主要有以下4个情况:

  • 顶部(Top):视图左上顶点到父控件上边界的距离;
  • 左边(Left):视图左上顶点到父控件左边界的距离;
  • 右边(Right):视图右下顶点到父控件左边界的距离;
  • 底部(Bottom):视图右下顶点到父控件上边界的距离。
    对应的获取方法:
  • getLeft():获取View到其父布局左边的距离。
  • getRight():获取View到其父布局左边的距离
  • getTop():获取View到其父布局顶边的距离
  • getBottom():获取View到其父布局底边的距离

3、几个重要的类:

Activity:
负责控制生命周期、处理事件。通过其他的触摸事件回调方法来与Window、View交互。但是不负责真正的View绘制。
Window:
视图的承载器。可以理解为整个的手机的屏幕,其唯一实现类的PhoneView,内部通过windowManager进行管理,将DecorVIew传递给ViewRoot进行绘制逻辑。
DecorView:
顶级VIew,也就是所有VIew 的根节点,本质上是一个FrameLayout的子类,包含标题栏、内容栏两部分。
ViewRoot:
连接器,负责连接windowManagerService和DecorView,同时负责真正的绘制逻辑。也就是onMesure() -> onLayout() -> onDraw();同时也负责wMS通讯,向decorWindow传递事件

其关系见图:

在这里插入图片描述

  • 来自Carson的文章

绘制前的准备工作:
从Activity的onCreate的setContentView开始:

  1. 创建window类,也就是创建对应的phoneWindow。
  2. 为phoneWindow创建对应的WindowManeger。
  3. 创建DecorView,同时为其设置相关的布局文件。
  4. 将DecorView放入到WindowManager中。
  5. 创建ViewRoot对象。
  6. 通过WIndowManager将DecorView传递给ViewRoot。
  7. 开始绘制。

绘制流程:
基本绘制流程:onMeasure() (确定大小)-> onLayout()(确定位置) -> onDraw()(实际绘制)。
onMeasure()

学习onMeasure()前要先了解两个基本的类:
ViewGroup.layoutParams: 指定View的高度和宽度以及其他布局参数。通过xml配置后得来。

MeasureSpec:测量规格类,用来设置view的大小。本质上是一个32位INT类型组成。包含两部分内容信息:
1、 测量模式(mode):占测量规格的前2位,主要分为三种:EXACTLY(精确模式)、AT_MOST(最大限定模式)、UNSPECIED(无限制)。
2、设置大小,也就是设置的精确大小。
三种模式的区别:(重要)
在这里插入图片描述

onMeasure的流程:

对于单一的View绘制:
整体绘制流程图:

在这里插入图片描述
1、measure():measure的入口,通过该入口开始执行measure流程,在此调用onMeasure()方法开始真正测量。
2、onMesure():测量的真正逻辑,在此进行真正的测量。
3、setMeasureDimension(): 保存测量的结果,一般来说onMeasure可能会执行多次,最后在此方法保存结果。

onMeasure()源码:

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
   // 参数说明:View的宽 / 高测量规格
   setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),  
                        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));  
   // setMeasuredDimension() :保存View宽/高的测量值
   // 也就是通过getDefaultSize方法传输宽度和高度的MeasureSpec后,把该数据进行保存,完成测量过程

getDefaultSize()源码:

  public static int getDefaultSize(int size, int measureSpec) {  

    // 参数说明:
    // size:提供的默认大小
    // measureSpec:宽/高的测量规格(含模式 & 测量大小)

    // 设置默认大小
    int result = size; 
        
    // 获取宽/高测量规格的模式 & 测量大小
    int specMode = MeasureSpec.getMode(measureSpec);  
    int specSize = MeasureSpec.getSize(measureSpec);  
  
    switch (specMode) {  
        // 模式为UNSPECIFIED时,使用提供的默认大小 = 参数Size
        case MeasureSpec.UNSPECIFIED:  
            result = size;  
            break;  

        // 模式为AT_MOST,EXACTLY时,使用View测量后的宽/高值 = measureSpec中的Size
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = specSize;  
            break;  
    }  

    // 返回View的宽/高值
    return result;  
  }    

对于的ViewGroup的绘制:
基本绘制流程图:
在这里插入图片描述

测量流程:
1、measure():measure的入口,通过该入口开始执行measure流程,在此调用onMeasure()方法开始真正测量。

2、onMesure():需要进行复写的方法。在此遍历所有的子VIew,计算出所有的子View的宽高,最后进行合并成最终的ViewGroup尺寸。同时还要在此执行保存尺寸的逻辑。

3、measureChidren():遍历所有的子View,并且调用measureChild()方法测量子View的大小。

4、measureChild():计算子View的大小,同时在此进行一次measure()的流程。

5、getChildMeasureSpec():获得子View的测量大小

6、setMeasureDimension(): 保存测量的结果,一般来说onMeasure可能会执行多次,最后在此方法保存结果。

onLayout()流程

流程图:
在这里插入图片描述

  • 来自Carson的图

onLayout的流程与onMeasure类似,也是通过计算layout的四个顶点,计算时若有子View计算子View的四个顶点位置,最后保存到 mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom)中

onDraw():

绘制的过程也是与onMeasure()流程相似,基本流程图:
在这里插入图片描述

1、draw():绘制的入口。
2、drawBackground():绘制背景。
3、onDraw():绘制本身的内容。
4、disPathDraw():绘制子View,这个不一定有。
5、onDrawScrollBars():绘制装饰边,例如颜色,滑动条等等。
也可以从源码看出来,绘制的过程中,主要流程:绘制背景->绘制本身->绘制子类->绘制装饰类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值