流式布局FlawLayout自定義view
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
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/276221.html
標籤:其他
上一篇:iOS安全之UIWebView 被拒的解決方案:用更安全的WKWebView替代UIWebView| 蓄力計劃
下一篇:安裝反射大師具體步驟與使用教程
