在历史搜索功能中,我们常用到一个瀑布流展示控件,用来展示我们的搜索记录,所以就自定义一个吧!
布局中代码示例
<com.example.mymodularization.measure.LinearCustom
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
我们的宽和高都是wrap_content,那么我们就应该首选对自定义的控件的meause进行测量
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var height = 0
var width = 0
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
// 测量我们的子view
measureChildren(widthMeasureSpec, heightMeasureSpec)
when (widthMode) {
// 我们的宽和高是根据我们的填充的数据来测量最后得到的,既然是wrap_content,那么我们的测量模式就是AT_MOST。
MeasureSpec.AT_MOST -> {
// 我们遍历子view,根据判断子view的宽度是否大于当前屏幕的宽度,得到我们最终的高度
for (i in 0 until childCount) {
val view = getChildAt(i)
width += view.measuredWidth
// 如果大于屏幕宽度,加上下一行的view高度
if (width > widthPixels) {
height += view.measuredHeight
}
// 提前对下一个view宽度测量,超过加上下一行高度
if (i + 1 < childCount) {
if (view.measuredWidth + width > widthPixels) {
height += view.measuredHeight
}
}
}
}
}
// 最终的测量就根据我们的屏幕宽度和测量到的高度
setMeasuredDimension(widthPixels, height)
}
既然测量好了,那么就要对我们view进行布局
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var left = 10
var top = 10
for (i in 0 until childCount) {
getChildAt(i).layout(
left, top, getChildAt(i).measuredWidth + left, getChildAt(i).measuredHeight + top
)
// 决定子view的左边缘距离
left += getChildAt(i).measuredWidth + 10
// 达到换行时,我们需要重新对左边距离和顶端距离进行初始化
if (left > widthPixels) {
left = 10
top += getChildAt(i).measuredHeight + 10
}
// 预防加上下一个view的宽度超过屏幕,提前对换行做处理
if (i + 1 < childCount) {
if (getChildAt(i + 1).measuredWidth + left > widthPixels) {
left = 10
top += getChildAt(i).measuredHeight + 10
}
}
}
}
添加数据
// 对外提供的添加数据的方法
fun addData(list: MutableList<String>) {
list.forEach { s ->
val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
val appCompatTextView = AppCompatTextView(context)
appCompatTextView.text = s
appCompatTextView.setTextColor(Color.RED)
appCompatTextView.setBackgroundDrawable(
context.resources.getDrawable(R.drawable.text_bg, null)
)
appCompatTextView.layoutParams = layoutParams
appCompatTextView.setOnClickListener {
Toast.makeText(context, "$s", Toast.LENGTH_SHORT).show()
}
// 内部调用了requsetLayout,会执行onmeause,onlayout
addView(appCompatTextView)
}
}
最终效果
思路就是,首选我们需要确定当前自定义的view的宽和高,确定宽和高后。我们就需要确定子view的位置,位置的处理需要注意换行,当满足一行或者在下一个view的内容超过屏幕,换行的处理。
以上demo仅供参考