最终效果
详细代码
class TagLayout : ViewGroup {
/**
* 所有view的集合
*/
private var mViews: MutableList<MutableList<View>> = mutableListOf()
/**
* 每一行存储的view
*/
private var mLineViews: MutableList<View> = mutableListOf()
/**
* 每一行view的最大高度
*/
private var mHeights: MutableList<Int> = mutableListOf()
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)
//避免多次执行onMeasure导致的问题
mViews = mutableListOf()
mLineViews = mutableListOf()
mHeights = mutableListOf()
//获取限制的值
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val paddingLeftRight = paddingLeft + paddingRight
val paddingTopBottom = paddingTop + paddingBottom
var lineWidth = 0 // 宽度为每一行view的宽度之和
var lineHeight = 0 // 高度为每一行view的最高值
var flowLayoutWidth = 0 //整体布局的宽度,取每一行view宽度的最大值
var flowLayoutHeight = 0 // 取每一行view最高值之和
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChild(child, widthMeasureSpec, heightMeasureSpec)
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
val childLayoutParams: MarginLayoutParams = child.layoutParams as MarginLayoutParams
val childWidth = child.measuredWidth
val childHeight = child.measuredHeight
val childMarginLeftRight = childLayoutParams.leftMargin + childLayoutParams.rightMargin
val childMarginTopBottom = childLayoutParams.topMargin + childLayoutParams.bottomMargin
if (lineWidth + paddingLeftRight + childWidth + childMarginLeftRight > widthSize) {
mViews.add(mLineViews)
mLineViews = mutableListOf()
flowLayoutWidth = flowLayoutWidth.coerceAtLeast(lineWidth)
flowLayoutHeight += lineHeight
mHeights.add(lineHeight)
lineWidth = 0
lineHeight = 0
}
mLineViews.add(child)
lineWidth += childWidth + childMarginLeftRight
lineHeight = lineHeight.coerceAtLeast(childHeight + childMarginTopBottom)
//处理最后一行显示问题,当遍历到最后一个view的时候,没有计算到整体中
if (i == childCount - 1) {
flowLayoutHeight += lineHeight
flowLayoutWidth = flowLayoutWidth.coerceAtLeast(lineWidth)
mHeights.add(lineHeight)
mViews.add(mLineViews)
}
}
//添加上padding
flowLayoutWidth += paddingLeftRight
flowLayoutHeight += paddingTopBottom
setMeasuredDimension(
if (widthMode == MeasureSpec.EXACTLY) widthSize else flowLayoutWidth,
if (heightMode == MeasureSpec.EXACTLY) heightSize else flowLayoutHeight
)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var currentX = paddingLeft
var currentY = paddingTop
for (i in 0 until mViews.size) {
//每一行最高的高度进行累加
val lineHeight = mHeights[i]
val lineViews = mViews[i]
for (lineView in lineViews) {
val childLayoutParams: MarginLayoutParams =
lineView.layoutParams as MarginLayoutParams
val left = currentX + childLayoutParams.leftMargin
val top = currentY + childLayoutParams.topMargin
val right = left + lineView.measuredWidth
val bottom = top + lineView.measuredHeight
//子view进行布局
lineView.layout(
left,
top,
right,
bottom
)
currentX += right + childLayoutParams.rightMargin
}
currentX = paddingLeft
currentY += lineHeight
}
}
//需要重写此方法,否则子view无法获取margin (child.layoutParams as MarginLayoutParams 会抛异常)
override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
return MarginLayoutParams(context, attrs)
}
}
小结
整体代码还是比较简单,但作为学习自定义ViewGroup还是十分不错的,不积跬步无以至千里!!!