type
status
date
slug
summary
tags
category
password
icon
布局流程
布局过程
- 确定每个 View 的位置和尺⼨
- 作⽤:为绘制和触摸范围做⽀持
- 绘制:知道往哪⾥绘制
- 触摸反馈:知道⽤户点的是哪⾥
流程
从整体看:
- 测量流程:从根 View 递归调⽤每⼀级⼦ View 的
measure()
⽅法,对它们进⾏测量
- 布局流程:从根 View 递归调⽤每⼀级⼦ View 的
layout()
⽅法,把测量过程得出的⼦ View 的位置和尺⼨传给⼦ View,⼦ View 保存
- 为什么要分两个流程? 因为可能会重复测量
从个体看,对于每个 View:
- 运⾏前,开发者在 xml ⽂件⾥写⼊对 View 的布局要求 layout_xxx
- ⽗ View 在⾃⼰的 onMeasure() 中,根据开发者在 xml 中写的对⼦ View 的要求,和⾃⼰的可⽤空间,得出对⼦ View 的具体尺⼨要求
- ⼦ View 在⾃⼰的 onMeasure() 中,根据⽗ View 的要求和⾃⼰的特性算出⾃⼰的期望尺⼨
- 如果是 ViewGroup,还会在这⾥调⽤每个⼦ View 的 measure() 进⾏测量
- ⽗ View 在⼦ View 计算出期望尺⼨后,得出⼦ View 的实际尺⼨和位置
- ⼦ View 在⾃⼰的 layout() ⽅法中,将⽗ View 传进来的⾃⼰的实际尺⼨和位置保存
- 如果是 ViewGroup,还会在 onLayout() ⾥调⽤每个⼦ View 的 layout() 把它们的尺⼨位置传给它们
尺寸的自定义
简单改写已有 View 的尺⼨
- 重写 onMeasure()
- ⽤
getMeasuredWidth()
和getMeasuredHeight()
获取到测量出的尺⼨
- 计算出最终要的尺⼨
- ⽤
setMeasuredDimension(width, height)
把结果保存
- 例⼦:SquareImageView
- 为什么不重写 layout()?
因为重新 layout() 会导致「不听话」(父View无法知道layout中做了哪些变化)
设置View的内边距的方法
完全⾃定义 View 的尺⼨
- 重写
onMeasure()
- 计算出⾃⼰的尺⼨
- MeasureSpec.EXACTLY → 精确值,所以就用父View传过来的值
- MeasureSpec.AT_MOST → 最大限制值,不允许超过
- MeasureSpec.UNSPECIFIED → 无限制
- 由于上一步中基本都是定好的规则,代码逻辑大部分是死的,所以官方封装了
resolveSize()
和resolveSizeAndState()
用于完成上一步操作,并修正结果
- 使⽤
setMeasuredDimension(width, height)
保存结果
- 例⼦:CircleView
Layout 的⾃定义
⾃定义 Layout
- 重写 onMeasure()
- 遍历每个⼦ View,测量⼦ View
- 测量完成后,得出⼦ View 的实际位置和尺⼨,并暂时保存
- 有些⼦ View 可能需要重新测量
- 测量出所有⼦ View 的位置和尺⼨后,计算出⾃⼰的尺⼨,并⽤
setMeasuredDimension(width, height)
保存
上面的策略子View的规则,基本上也是写死的一套规则,所以View中提供了一个方法
- 重写 onLayout()
- 遍历每个⼦ View,调⽤它们的 layout() ⽅法来将位置和尺⼨传给它们
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/d0e831fb-6890-4c87-a041-f064ec599b97
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。