Android 自定义控件的 measure, layout

Android 自定义控件的 measure, layout

Android 自定义 View 一般都要写 测量, 摆放

在 onMeasure 里面测量出自己的宽高, 然后父控件会根据自己测量出来的宽高来进行摆放(layout)

如果不按照父容器的约束来, 就会出问题

重写了 layout 方法, 摆放的很大, 但是父容器那里的尺寸并没有改, 所以其他的 view 就会有重叠

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.example.lsn13_layout.views.OneHundredView
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:background="#FF0000" />

    <View
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:background="#00FF00" />
</LinearLayout>


class OneHundredView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

    override fun layout(l: Int, t: Int, r: Int, b: Int) {
        super.layout(l, t, r + dp2px(100F).toInt(), b + dp2px(100F).toInt())
    }
}

在这里插入图片描述

流程

  • 从整体看:

    • 测量流程: 从根 View 递归调用每一级子 Viewmeasure() 方法, 对它们进行测量

    • 布局流程: 从根 View 递归调用每一级子 Viewlayout() 方法, 把测量过程得出的子 View 的位置和尺寸传给子 View , 子 View 保存

    • 为什么要分两个流程?

  • 从个体看, 对于每个 View :

    • 1、运行前, 开发者在 xml 文件里写入对 View 的布局要求 layout_xxx

    • 2、父 View 在自己的 onMeasure() 中, 根据开发者在 xml 中写的对子 View 的要求, 和自己的可用空间, 得出对子 View 的具体尺寸要求

    • 3、子 View 在自己的 onMeasure() 中, 根据自己的特性算出自己的期望尺寸

      • 如果是 ViewGroup, 还会在这里调用每个子 Viewmeasure() 进行测量
    • 4、父 View 在子 View 计算出期望尺寸后, 得出子 View 的实际尺寸和位置

    • 5、子 View 在自己的 layout() 方法中, 将父 View 传进来的自己的实际尺寸和位置保存

      • 如果是 ViewGroup , 还会在 onLayout() 里调用每个子 Viewlayout() 把它们的尺寸位置传给它们

自定义控件一般有三种情况

1、集成已有的 View , 简单改写它们的尺寸: 重写 onMeasure()
  • 重写 onMeasure()

  • getMeasuredWidth()getMeasuredHeight() 获取到测量出的尺寸

  • 计算出最终要的尺寸

  • setMeasureDimension(width, height) 把结果保存

2、对自定义 View 完全进行自定义尺寸计算: 重写 onMeasure()
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val size = (PADDING + RADIUS) * 2
    val measuredWidth = resolveSizeAndState(size.toInt(), widthMeasureSpec, 0)
    val measuredHeight = resolveSizeAndState(size.toInt(), heightMeasureSpec, 0)
    setMeasuredDimension(measuredWidth, measuredHeight)
}
  • 1、重写 onMeasure

  • 2、计算出自己的尺寸

  • 3、用 resolveSize() 或者 resolveSizeAndState() 修正结果

    • resolveSize() 内部实现

      • 首先用 MeasureSpec.getMode(measureSpec)MeasureSpec.getSize(measureSpec) 取出父容器对自己的尺寸限制类型和具体限制尺寸;

      • 如果 measureSpecmodeMeasureSpec.EXACTLY , 表示父 View 对子 View 的尺寸做出了精确限制, 所以就放弃计算出的 size , 直接选用 measureSpecsize;

      • 如果 measureSpecmodeMeasureSpec.AT_MOST , 表示父 View 对子 View 的尺寸只限制了上限, 需要看情况:

        • 如果计算出的 size 不大于 measureSpec 中显示的 size , 表示尺寸没有超出限制, 所以选用计算出的 size;

        • 而如果计算出的 size 大于 measureSpec 中限制的 size , 表示尺寸超限了, 所以选用 measureSpecsize , 并且在 resolveSizeAndState() 中会添加标志 MEASURED_STATE_TOO_SMALL (这个表示可以辅助父View做测量和布局的计算);

      • 如果 measureSpecmodeMeasureSpec.UNSPECIFIED , 表示父 View 对子 View 没有任何尺寸限制, 所以直接选用计算出的 size , 忽略 measureSpec 中的 size.

  • 4、用 setMeaduredDimension(width, height) 来保存结果

3、自定义 Layout: 重写 onMeasure()onLayout()
  • 首先用 measureChildWithMargins(child, widthMeasureSpec, paddingStart + paddingEnd, heightMeasureSpec, heightUsed + paddingTop + paddingBottom) 测量子控件的宽高

  • 如果已经用掉的宽度加上子控件的宽度超过了父容器给的宽度, 就要换行, 然后重新测量

  • 最后用 resolveSizeAndState() 来纠正宽高

  • 1、重写 onMeasure()

    • 1、遍历每个子 View , 用 measureChildWithMargins() 测量

      • 需要重写 generateLayoutParams() 并返回 MarginLayoutParams

      • 换行处的 View 需要重新测量

      • 测量完成后, 得出子 View 的尺寸和位置, 并保存

    • 2、测量出所有的子 View 的尺寸后, 计算出自己的尺寸, 最后用 setMeasuredDimension(measuredWidth, meaduredHeight) 保存

  • 2、重写 onLayout()

    • 遍历每个子 View , 调用它们的 layout() 方法将它们的尺寸和位置传进去
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值