Magren

Magren

Idealist & Garbage maker 🛸
twitter
jike

Android自定义view的定义

自定义 View 就是通过继承 View 或者 View 的子类,并在新的类里面实现相应的处理逻辑(重写相应的方法),以达到自己想要的效果。

分类#

  • 自定义 ViewGroup:自定义 ViewGroup 一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自 ViewGroup 或各种 Layout,包含有子 View。
  • 自定义 view: 在没有现成的 View,需要自己实现的时候,就使用自定义 View,一般继承自 View,SurfaceView 或其他的 View,不包含子 View。

构造函数#

无论是我们继承系统 View 还是直接继承 View,都需要对构造函数进行重写,构造函数有多个,至少要重写其中一个才行。

public class TestView extends View {
    /**
     * 在java代码里new的时候会用到
     * @param context
     */
    public TestView(Context context) {
        super(context);
    }

    /**
     * 在xml布局文件中使用时自动调用
     * @param context
     */
    public TestView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 不会自动调用,如果有默认style时,在第二个构造函数中调用
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    /**
     * 只有在API版本>21时才会用到
     * 不会自动调用,如果有默认style时,在第二个构造函数中调用
     * @param context
     * @param attrs
     * @param defStyleAttr
     * @param defStyleRes
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}

绘制过程#

  • 测量
  • 布局
  • 绘制
  • 提供接口

测量阶段#

  • View 的父 View 通过调用 View 的 measure () 方法将父 View 对 View 尺寸要求传进来。
  • View 的 measure () 方法进行一些前置和优化工作
  • 调用 onMeasure () 方法,在方法中根据业务需求进行相应的逻辑处理。在自定义 ViewGroup 的 onMeasure () 方法中,ViewGroup 会递归调用子 View 的 measure () 方法,并通过 measure () 将 ViewGroup 对子 View 的尺寸要求 对自己的尺寸要求和自己的可用空间计算出自己对子 View 的尺寸要求)传入,对子 View 进行测量,并把测量结果临时保存,以便在布局阶段使用。ViewGroup 会根据子 View 的实际尺寸计算出自己的期望尺寸,并通过 setMeasuredDimension () 方法告知父 View(ViewGroup 的父 View) 自己的期望尺寸。
  • 方法里调用 **setMeasuredDimension ()** 方法告知父 View 自己的期望尺寸

onMeasure () 计算 View 的期望尺寸的方法:

  • 参考父 View 的对 View 的尺寸要求和实际业务需求计算出 View 的期望尺寸:
    • 解析 widthMeasureSpec;
    • 解析 heightMeasureSpec;
    • 将「根据实际业务需求计算出 View 的尺寸」根据「父 View 的对 View 的尺寸要求」进行相应的修正得出 View 的期望尺寸(通过调用 resolveSize () 方法)
  • 通过 setMeasuredDimension () 保存 View 的期望尺寸(实际上是通过 setMeasuredDimension () 告知父 View 自己的期望尺寸

onMeasure() :

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthsize  MeasureSpec.getSize(widthMeasureSpec);      //取出宽度的确切数值
    int widthmode  MeasureSpec.getMode(widthMeasureSpec);      //取出宽度的测量模式
    
    int heightsize  MeasureSpec.getSize(heightMeasureSpec);    //取出高度的确切数值
    int heightmode  MeasureSpec.getMode(heightMeasureSpec);    //取出高度的测量模式
}

widthMeasureSpec 和 heightMeasureSpec 这两个 int 类型的参数其实不是宽和高, 而是由宽、高和各自方向上对应的测量模式来合成的一个值

布局阶段#

  1. 父 View 通过调用 View 的 Layout 方法将 View 的实际尺寸传给 View
  2. View 在 Layout 方法中调用 setFramne () 方法保存
  3. setFrame () 方法中又会调用 onSizeChanged () 方法告知开发者 View 的尺寸修改了
  4. View 的 Layout () 方法调用 View 的 onLayout () 方法,它是一个空实现。但是在 ViewGroup 中,这里会调用子 View 的 Layout () 方法,将子 View 的实际尺寸传给他们,让子 View 保存实际尺寸。在自定义 ViewGroup 中需要重写该方法。

绘制阶段#

  • draw () ,总调度方法,会调用绘制背景的方法,绘制主题的方法,绘制前景的方法和绘制子 View 的方法
  • onDraw () ,绘制 View 主体内容的方法
  • dispatchDraw (),绘制子 View 的方法,在自定义 ViewGroup 中会调用 ViewGroup.drawChild () 方法,这个方法会调用每个子 View 的 View.draw ()
  • drawBackground (),绘制背景的方法,不可重写,只能通过 xml 布局文件或者 setBackground () 方法来设置背景。
  • onDrawForegound (),绘制 View 前景的方法,绘制主体内容之上的东西的时候在该方法中实现。

提供接口#

写一些控制 View 或者监听 View 某些状态。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。