Android — 自定义View(一)之基础知识

前言

记得之前写过一篇自定义View的文章,当时也是尝尝鲜。今天看到一篇自定义View系列文章,也是蛮新的,决定要认认真真的系统学习一遍。原文地址:http://www.gcssloop.com/customview/CustomViewIndex/ 当然,系列中有些东西我觉得很没必要,我想学最实用的知识。(可能我的看法有些片面)还有文章中讲的有些我不明白的地方,我在看看别的博客在加上自己的理解写了这篇博客。这篇文章再详细记录一下学习中非常重要的知识点和要注意的东西。整体目录如下:

  1. View坐标系
  2. 自定义View绘制流程概述
  3. 总结流程关键字以及作用

那准备好了吗?

View 坐标系

首先要明确的是:Android中的坐标系统是以左上角为坐标原点,向右是X轴正方向,向下是Y轴正方向。View的坐标系统是相对于父控件而言的。

View 获取自身宽高:

  • getHeight() 获取View自身高度
  • getWidth() 获取View自身宽度

View 自身坐标:

  • getTop() 获取View自身顶边到其父布局顶边的距离
  • getLeft() 获取View自身左边到其父布局左边的距离
  • getRight() 获取View自身右边到其父布局左边的距离
  • getBottom() 获取View自身底边到其父布局顶边的距离

MotionEvent提供的方法:

  • getX() 获取点击事件距离控件左边的距离,即视图坐标
  • getY() 获取点击事件距离控件顶边的距离,即视图坐标
  • getRawX() 获取点击事件距离整个屏幕左边距离,即绝对坐标
  • getRawY() 获取点击事件距离整个屏幕顶边的距离,即绝对坐标

说了那么多,还是来一图胜千言吧。

自定义View绘制流程

盗一张图:

几个非常重要的函数:

  • 构造函数

    构造函数是View的入口,可以用于初始化一些内容,和获取自定义属性。View的构造函数有四种重载,而有四个参数的构造函数在API21的时候才添加上,暂不考虑。而有三个参数的构造方法中第三个参数是默认的Style,这里默认的Style是指它在当前Application或Activity所用Theme中的默认Style,且只有在明确调用的时候才会生效。

    需要注意的是,即使你在View中使用了Style这个属性也不会调用三个参数的构造方法,所以调用的依旧是两个参数的构造方法。

    调用一个参数的构造方法:

    1
    CustomView view = new CustomView(this);

    调用两个参数的构造方法:

    1
    2
    3
    4
    //在layout文件中 - 格式为: 包名.View名
    <top.omooo.CustomView
    android:layout_width"wrap_content"
    android:layout_height"wrap_content"/>
  • onMeasure() 测量View大小

    View的大小不仅由自身所决定,同时也会受到父控件的影响,为了我们的控件能更好的适应各种情况,一般我们会自己进行测量。

1
2
3
4
5
6
7
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽度的确切数值
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//获取宽度的测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
}
onMeasure()函数中有两个参数,很显然它们是和宽高相关的,**但是实际上它们不是宽和高** ,而是由宽高和各自方向上对应的测量模式来合成的一个值。 测量模式一共有三种,被定义在Android中的View类的一个内部类View.MeasureSpec中。 注意,如果对View的宽高进行修改了,不要调用super.onMeasure();而是调用setMeasuredDimension() 这个函数。
  • onSizeChanged() 确定View的大小

    为什么View的大小测量完了,还要再次确定View的大小呢?这是因为View的大小不仅由View本身控制,而且还受父控件的影响。所以我们在确定View的大小的时候最好使用系统提供的onSizeChanged()回调函数。

1
2
3
4
5
6
7
8
9
10
/**
* @param w View最终的宽
* @param h View最终的高
* @param oldw 上一次宽
* @param oldh 上一次高
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
  • onLayout() 确定子View布局位置

    在自定义ViewGroup中的时候,会用它确定子View的位置。onLayout() 一般是循环取出子View,然后计算各个子View的坐标值,然后用以下函数设置子View的位置。

1
2
3
4
5
6
/**
* @param l getLeft()
* @param t getTop()
* @param r getRight()
* @param b getBottom()
*/
@Override public void layout(int l, int t, int r, int b) { super.layout(l, t, r, b); } ​
  • onDraw() 绘制内容

    是实际绘制的部分,使用的是Canvas绘图。

1
2
3
4
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
  • 另外

    自定义View之后,一般会对外暴露一些接口,用于控制View的状态或监听View的变化等等。

总结

步骤 关键字 作用
1 构造函数 View初始化
2 onMeasure() 测量View大小
3 onSizeChanged() 确定View大小
4 onLayout() 确定子View布局
5 onDraw() 实际绘制内容
6 提供接口 控制View或监听View状态
我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?