1、自定义ViewGroup时通常只需要关注两个方法分别是onMeasure和onLayout。完整代码如下:
package com.example.view2
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
/**
* 项目名称 View2
* 创建人
* 创建时间 10/2/21 10:22 AM
*
**/
class MyFirstGroupView:ViewGroup{
val TAG = "MyFirstGroupView"
var offset = 100
constructor(context: Context?) : super(context) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
// 1、首选需要根据父容器的MeasureSpec和子View的布局参数测量子View by on 10/2/21 10:31 AM
val childCount = getChildCount()
for(i in (0..childCount-1)){
val view:View = getChildAt(i)
view?.let {
val params = view.layoutParams
val measureSpecWidthOfView = getChildMeasureSpec(widthMeasureSpec, 0, params.width)
val measureSpecHeightOfView = getChildMeasureSpec(
heightMeasureSpec,
0,
params.height
)
view.measure(measureSpecWidthOfView, measureSpecHeightOfView)
}
}
// 2、计算ViewGroup自己的尺寸 by on 10/2/21 10:50 AM
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
var width = 0
var height = 0
when(widthMode){
MeasureSpec.EXACTLY -> {
width = widthSize
}
MeasureSpec.AT_MOST -> {
for (i in 0..childCount) {
width = Math.max(height, getChildAt(i).measuredWidth + i * offset)
}
}
MeasureSpec.UNSPECIFIED -> {
width = widthSize
}
}
when(heightMode){
MeasureSpec.EXACTLY -> {
height = heightSize
}
MeasureSpec.AT_MOST -> {
for (i in 0..childCount) {
height = height + getChildAt(i).measuredWidth
}
}
MeasureSpec.UNSPECIFIED -> {
height = heightSize
}
}
// 3、保存自身的尺寸 by on 10/2/21 10:55 AM
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val childCount = childCount
var heightOffset = 0
for(i in 0..childCount-1){
var view = getChildAt(i)
view.layout(
offset * i,
heightOffset,
view.measuredWidth + offset * i,
view.measuredHeight + heightOffset
)
heightOffset = heightOffset + view.measuredHeight
}
}
}
测量时可分为3步:
- 根据父容器的MeasureSpec和自身的布局参数测量子View的MeasureSpec。
- 根据父容器的MeasureSpec(其实是SpecMode)和子View的大小确定父容器的大小。
- 设置父容器的大小
布局时比较简单,其实就是根据预先设定的规则,计算子View的left、top、right和bottom,将其放置到正确的位置
使用
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<com.example.view2.MyFirstGroupView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是第一行"></TextView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="这是一个EditText"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这是一个button"/>
</com.example.view2.MyFirstGroupView>
</androidx.constraintlayout.widget.ConstraintLayout>
运行结果如下: