package com.example.lsn_compose.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import com.blankj.utilcode.util.ConvertUtils.dp2px
import kotlin.math.max
class FlowLayout(context: Context) : ViewGroup(context) {
private val mHorizontalSpace = dp2px(16.0F)
private val mVerticalSpace = dp2px(8.0F)
private var allLinesViews = mutableListOf<List<View>>()//记录所有的行,用于layout
private var lineHeights = mutableListOf<Int>()
constructor(context: Context, attributes: AttributeSet) : this(context)
constructor(context: Context, attributes: AttributeSet, defStyleAttr: Int) : this(context)
private fun clearMeasureParams() {
allLinesViews.clear()
lineHeights.clear()
}
@SuppressLint("DrawAllocation")
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
clearMeasureParams()//防止频繁创建空间而发生OOM
//本View的宽高
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
var lineViews = mutableListOf<View>()//记录一行中已经添加了的View
var usedWidth = 0//记录已经用了的宽度
var lineHeight = 0//记录已经用了的行高
var childNeededParentWidth = 0//所有子view加起来需要父控件给的宽度
var childNeededParentHeight = 0//所有子view加起来父控件需要给的高度
//先度量所有子view的宽高--->view是树形结构,递归度量
for (i: Int in 0 until childCount) {
//度量子view的宽高
children.elementAt(i).measure(
getChildMeasureSpec(
widthMeasureSpec,
paddingLeft + paddingRight,
children.elementAt(i).layoutParams.width
),
getChildMeasureSpec(
heightMeasureSpec,
paddingTop + paddingBottom,
children.elementAt(i).layoutParams.height
)
)
//获取子view的宽高
val childMeasuredWidth: Int = children.elementAt(i).measuredWidth
val childMeasuredHeight: Int = children.elementAt(i).measuredHeight
//换行
if (usedWidth + childMeasuredWidth + mHorizontalSpace >= width) {
allLinesViews.add(lineViews)
lineHeights.add(lineHeight)
//宽度取需要父控件给的宽度和已经使用的宽度的最大值--->通常情况下,如果没到边界,子view所需要父控件所给与的宽度就是子view已经使用的宽度
childNeededParentWidth = max(childNeededParentWidth, usedWidth + mHorizontalSpace)
//高度就是一直累加
childNeededParentHeight += lineHeight + mVerticalSpace
lineViews = mutableListOf()//是否能用clear?
usedWidth = 0
lineHeight = 0
}
//流式布局是分行布置的,所以要先把每个已经算好的子view添加到每一行中,然后再把每一行添加到所有行中--->两个list
lineViews.add(children.elementAt(i))
//每行的宽高
usedWidth += childMeasuredWidth + mHorizontalSpace
lineHeight = max(lineHeight, childMeasuredHeight)
//处理最后一行
if (i == childCount - 1) {
allLinesViews.add(lineViews)
lineHeights.add(lineHeight)
childNeededParentWidth = max(childNeededParentWidth, usedWidth + mHorizontalSpace)
childNeededParentHeight += lineHeight + mVerticalSpace
}
}
//再度量自身的viewGroup宽高---->作为一个vp,他自己也是一个view,它的大小也需要根据父view的大小来一起度量才能决定
val widthViewMode: Int = MeasureSpec.getMode(widthMeasureSpec)
val heightViewMode: Int = MeasureSpec.getMode(heightMeasureSpec)
val realWidth = if (widthViewMode == MeasureSpec.EXACTLY) width else childNeededParentWidth
val realHeight =
if (heightViewMode == MeasureSpec.EXACTLY) height else childNeededParentHeight
//度量自己
setMeasuredDimension(realWidth, realHeight)
}
override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
//1.先获得此view的左上坐标
var currentLeft = paddingLeft
var currentTop = paddingTop
// val allLineCount = allLinesViews.size
//2.取出每一行都进行位置计算
for (i in allLinesViews.indices) {
val lineHeight = lineHeights[i]
//3.取出每一行的每个element进行位置计算
val lineView = allLinesViews[i]
for (j in lineView.indices) {
val view = lineView[j]
val left = currentLeft
val top = currentTop
val right = left + view.measuredWidth
val bottom = top + view.measuredHeight
view.layout(left, top, right, bottom)
//移动坐标,准备行内下一个元素的放置
currentLeft = right + mHorizontalSpace
}
currentTop += lineHeight + mVerticalSpace
//一行放置完毕,需要重置左边坐标
currentLeft = paddingLeft
}
}
}
前言 随着我们vue3.0的出现,我们的ui组件库也有了一些变化,像我们的旧版的elem...
% Function DownloadTime(intFileSize, strModemType) Dim TimeInSeconds, Modem...
有时,主引导记录(MBR)可能会损坏或配置错误,从而导致Windows 10无法正确加载,...
BMP图像的移动 将BMP图像向各个方位移动下面我用纯C语言实现这个功能 建模 将图...
Excel中分组排序只需要对数据进行升序降序,再利用if函数添加排序序号,即可筛选...
文章目录 引言 I、 判断是否插了耳机 引言 I、 判断是否插了耳机 /** 1. AudioTo...
这篇文章仅做分享,因为刚刚涉猎这一块,在慢慢学习,不好做过多讲解,毕竟也是...
序列化和反序列化相信大家都经常听到,也都会用, 然而有些人可能不知道:.net为...
网站上的文章用什么存储?使用Oralce用CLOB存储,Java操作CLOB的方法网上很多,...
[例3.19] 查全体学生的姓名及其出生年份。 select Sname , 2021 - Sage from stu...