美文网首页
自定义View基础概述

自定义View基础概述

作者: w达不溜w | 来源:发表于2022-02-21 12:55 被阅读0次
1.自定义View的基本方法
  • onMeasure():测量视图大小
  • onLayout():确定View位置,进行页面布局
  • onDraw():绘制视图
2.View类的构造函数

View类是Android中各组件的基类,有4个构造函数

class CustomView : View {
    //用于代码构造View()
    constructor(context: Context?) : super(context)

    //用于xml中构造View,使用默认style(AttributeSet对应自定义属性值集合)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    //用于xml中构造View,使用特定style
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )
    //用于xml中构造View,使用特定style,特定资源 (不常用)
    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes)
}
3.View视图结构
View视图结构.png
  • PhoneWindow:负责管理界面显示和事件响应,是Activity与View系统交互的接口
  • DecorView:Android视图树的根节点,继承FrameLayout,作为整个视图容器来使用
  • measure, layout和draw过程,都是从View树的根节点开始,自上而下遍历(即树形递归),最终确定整个View树的相关属性
4.Android坐标系
Android坐标系.png

View的位置是相对于父控件而言的

  • getTop() 获取子View左上角到父View顶部的距离
  • getLeft() 获取子View左上角到父View左侧的距离
  • getBottom() 获取子View右下角到父View顶部的距离
  • getRight() 获取子View右下角到父View左侧的距离

MotionEvent中 :

event.getX()/event.getY():触摸点相对于其所在View坐标系的坐标
event.getRawX()/event.getRawY():触摸点相对于屏幕坐标系的坐标

5.MeasureSpect(重点)

整个测量过程是从根节点开始的遍历过程,在这个过程中父View需要告诉子View具体的模式和宽高值(对子View是一种约束,子View需要在允许的范围内绘制),最终android用一个int类型来表示模式和值。int占4个字节,32位,高2位存测量模式(mode),低30位存测量大小(size)

测量模式 说明
EXACTLY 确切的size
AT_MOST 父View为子View指定一个最大size,子View不能大于该size
UNSPECIFIED 父View不约束子View(即子View可以取任意size),系统内部使用(如RecyclerView、ScrollView)

MeasureSpec类就是⽤来处理这些事情的,子View的MeasureSpect值是根据子View的布局参数(LayoutParams)和父容器的MeasureSpect值计算得到的:

/**
    * 将父控件的测量规格和子View的布局参数计算得到一个最符合子View的测量规格
  *
  * @param spec  父控件的测量规格
  * @param padding 父控件里已经占用的大小
  * @param childDimension 子View布局LayoutParams里的尺寸
  * @return 子View的测量规格
  */
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  //父控件的测量模式
  int specMode = MeasureSpec.getMode(spec);
  //父控件的测量大小
  int specSize = MeasureSpec.getSize(spec);
    //获取父控件的size与padding的差值(也就是父控件剩余大小),若小于0则返回0
  int size = Math.max(0, specSize - padding);
    //定义返回值存储变量size
  int resultSize = 0;
  //定义返回值存储变量mode
  int resultMode = 0;
  //依据父控件的mode进行分支逻辑处理
  switch (specMode) {
      //父控件测量有精确尺寸
    case MeasureSpec.EXACTLY:
      if (childDimension >= 0) {
        //①如果child的布局参数有固定值(如android:layout_width="100dp"),那么child的size也可以确定下来(100dp),mode为EXACTLY,size为自己的大小
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {
        //②如果child的布局参数是math_parent(也就是child想要占满父控件),而此时父控件是精确模式,有精确尺寸,都给你,child也确定下来了,mode为EXACTLY,size为父控件size
        resultSize = size;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {
        //③如果child的布局参数是wrap_content(也就是child想要根据自己的逻辑决定自己的大小),那就自己决定,不过大小不能大于父控件,所以child的model为AT_MOST,size为父控件size
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;

      //父控件测量模式是AT_MOST,也就是父控件和还不知道自己的大小,但是不能超过size
    case MeasureSpec.AT_MOST:
      if (childDimension >= 0) {
        //④child能确定自己大小,自己确定
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {
        //⑤child想要和父控件一样大,但是父控件自己不确定自己的大小,所以child的size的上限是父控件size
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {
        //⑥child想要根据自己逻辑确定大小,size上限为父控件的size
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;

      //UNSPECIFIED一般用于系统内部多次measure的情况下,如RecyclerView,ScrollView测量子View给的就是UNSPECIFIED,我们暂时不关注
      // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
      if (childDimension >= 0) {
        // Child wants a specific size... let him have it
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {
        // Child wants to be our size... find out how big it should
        // be
        resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
        resultMode = MeasureSpec.UNSPECIFIED;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {
        // Child wants to determine its own size.... find out how
        // big it should be
        resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
        resultMode = MeasureSpec.UNSPECIFIED;
      }
      break;
  }
  //noinspection ResourceType
  return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
小结: MeasureSpect测量模式.png

相关文章

网友评论

      本文标题:自定义View基础概述

      本文链接:https://www.haomeiwen.com/subject/batilrtx.html