引入
我們?cè)陂_發(fā)過程中多多少少都會(huì)遇到這種需求:需要一個(gè)容器來容納一堆不規(guī)則的控件,當(dāng)容器寬度不夠的時(shí)候,新添加的控件會(huì)調(diào)到下一行顯示。但是,系統(tǒng)提供的容器中不帶有這樣的功能的容器,為滿足這樣的需求,我們就需要自定義ViewGroup。

流式布局
自定義ViewGroup的創(chuàng)建方法
- 1 重寫VIewGroup的構(gòu)造方法
通常需要context和attrs兩個(gè)參數(shù)
constructor(context: Context,attrs:AttributeSet?):super(context,attrs){
}
- 2繼承onMeasure和onLayout方法
onMeasure 對(duì)控件進(jìn)行測量
onLayout 進(jìn)行布局
一些重要的方法
getChildAt()獲取某一個(gè)子控件
childcount:獲取子控件的個(gè)數(shù)
MeasureSpec:可以做與尺寸、模式相關(guān)的操作,如
//獲取父容器的限制尺寸
val parentWidthSize = MeasureSpec.getSize(widthMeasureSpec)
//獲取父類的模式
MeasureSpec.getMode(widthMeasureSpec)
setMeasuredDimension:設(shè)置父容器尺寸,在做流式布局時(shí),通常先確定子控件的尺寸最后設(shè)置父容器尺寸。
getChildMeasureSpec與child.measure:
//確定子控件的measureSpec
val widthSpec = getChildMeasureSpec(widthMeasureSpec,2*space,lp.width)
val heightSpec = getChildMeasureSpec(heightMeasureSpec,2*space,lp.height)
child.measure(widthSpec,heightSpec)
除了上方的child.measure還可以使用measureChild,兩者在大部分情況下的沒有區(qū)別。
代碼詳情
package com.example.falltype
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import kotlin.math.max
/**
*@Description
*@Author PC
*@QQ 1578684787
*/
class MyViewGroup:ViewGroup{
//間距
private val space = 30
//容器中的所有控件
private var totalLineViews = mutableListOf<MutableList<View>>()
private var allLineHeight = mutableListOf<Int>()
constructor(context: Context,attrs:AttributeSet?):super(context,attrs){
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//測量自己 -> 提供的子控件進(jìn)行測量自己的(限制條件)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//獲取父容器的限制尺寸
val parentWidthSize = MeasureSpec.getSize(widthMeasureSpec)
val parentHeightSize = MeasureSpec.getSize(heightMeasureSpec)
//記錄當(dāng)前這一行的高度和寬度
var currentLineWidth = space
var currentLineHeight = 0
//當(dāng)前最大寬度/高度(最終父容器的尺寸)
var resultWidth = 0
var resultHeight = space
//記錄當(dāng)前這一行的所有子視圖
var lineViews = mutableListOf<View>()
for(i in 0 until childCount){
//確定子控件
val child = getChildAt(i)
//獲取子控件的布局參數(shù)(xml中設(shè)置的layout-width layout-height)
val lp = child.layoutParams
//確定子控件的measureSpec
val widthSpec = getChildMeasureSpec(widthMeasureSpec,2*space,lp.width)
val heightSpec = getChildMeasureSpec(heightMeasureSpec,2*space,lp.height)
child.measure(widthSpec,heightSpec)
//判斷這個(gè)控件是當(dāng)前行還是下一行
if (currentLineWidth + child.measuredWidth + space <= parentWidthSize){
//添加到當(dāng)前行
lineViews.add(child)
//改變當(dāng)前行的寬度
currentLineWidth += child.measuredWidth + space
//改變當(dāng)前行的寬度
currentLineHeight = max(currentLineHeight,child.measuredHeight)
}else{
//先保存上一行的數(shù)據(jù)
totalLineViews.add(lineViews)
//確定最大寬度
resultWidth = max(resultWidth,currentLineWidth)
//確定最大高度
resultHeight += currentLineHeight + space
//保存上一行的高度
allLineHeight.add(currentLineHeight)
//重置
lineViews = mutableListOf()
//將該控件添加到新的一行
lineViews.add(child)
currentLineHeight = child.measuredHeight
currentLineWidth = space + child.measuredWidth
}
//判斷是否還有最后一行
if (lineViews.size > 0 ){
//先保存上一行的數(shù)據(jù)
totalLineViews.add(lineViews)
//確定最大寬度
resultWidth = max(resultWidth,currentLineWidth)
//確定最大高度
resultHeight += currentLineHeight + space
allLineHeight.add(currentLineHeight)
}
}
//設(shè)置父容器的尺寸
setMeasuredDimension(resultWidth,resultHeight)
}
override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
var left = space
var top = space
var right = 0
var bottom = 0
val row = totalLineViews.size
for (i in 0 until row){
//遍歷每一行
val count = totalLineViews[i].size
for (j in 0 until count){
//取出每一行的控件
val child = totalLineViews[i][j]
right = left+child.measuredWidth
bottom = top+child.measuredHeight
child.layout(left,top,right,bottom)
left += child.measuredWidth+space
}
//確定下一行的top
top += allLineHeight[i] + space
//下一行的left從頭開始
left = space
}
}
}