效果
前言
我们在很多应用的搜索模块都会看到如上样式的热搜词标签,直接点击标签就可以进行搜索。现在已经有一些开源库和自定义 View 可以实现这样的效果,个人觉得那样还是麻烦了一些,下面介绍一下直接使用 FrameLayout 和 TextView 实现的做法。这种流式布局的关键在于每一行末尾控件的换行,需要在平铺控件时计算剩余空间宽度能否容下新控件,如果无法容下新控件则需要进行换行。
布局代码。
热搜词布局:
<FrameLayout
android:id="@+id/hot_words"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="56dp">
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/hot_words_select"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingRight="12dp"
android:paddingBottom="7dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/hot_words_select"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingRight="12dp"
android:paddingBottom="7dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="15sp" />
//此处省略8段类似TextView代码
</FrameLayout>
调整标签位置
通过代码调整每个 TextView 的位置,以下是核心代码:
private fun initData() {
val view = LayoutInflater.from(mContext).inflate(R.layout.fragment_search_hot_words, frameLayout, false)
val text1: TextView = view.findViewById(R.id.text1)
val text2: TextView = view.findViewById(R.id.text2)
//此处省略8行类似代码
views.add(text1)
views.add(text2)
//此处省略8行类似代码
frameLayout.removeAllViews()
frameLayout.addView(view)
//获取当前屏幕实际宽度(px)
val w = mContext.resources.displayMetrics.widthPixels
var xDistance = -1
var yDistance = 0
//标签间隔16dp
val distance = dip2px(mContext, 16f)
var i = 0
while (i < 10) {
views[i].setOnClickListener(this@SearchHotWordFragment)
views[i].text = texts[i]
if (xDistance == -1) {
//每行的第一个标签
xDistance = 0
//设置该 View 位置
WidgetController.setLayout(views[i], xDistance, yDistance)
i++
continue
}
//获取前一个标签宽度+16dp作为下一个标签横坐标
xDistance += WidgetController.getWidth(views[i - 1]) + distance
if (xDistance + WidgetController.getWidth(views[i]) + distance > w) {
//加上新标签的宽度大于屏幕宽度时换行
xDistance = -1
//换行时y坐标向下一行
yDistance += 120
continue
}
WidgetController.setLayout(views[i], xDistance, yDistance)
i++
}
}
代码注释很详细,在此就不再多说。代码中用到的 setLayout() 方法具体代码如下:
/*
* 设置控件所在的位置YY,并且不改变宽高,
* XY为绝对位置
*/
fun setLayout(view: View, x: Int, y: Int) {
val margin = ViewGroup.MarginLayoutParams(view.layoutParams)
margin.setMargins(x, y, 0, 0)
val layoutParams = FrameLayout.LayoutParams(margin)
view.layoutParams = layoutParams
}
总结
这样就完成了我们想要的流式布局,其实过程很简单,主要是要处理换行的布局问题。我们还能进一步对热搜标签定制样式。比如底部颜色和形状。本文实现流式布局的代码详见SearchHotWordFragment.kt。
代码来自我的开源项目:喜马拉雅Kotlin。一个模仿企鹅 FM 界面的 Android 应用—喜马拉雅Kotlin。完全使用 Kotlin 开发。它是我学习 Kotlin 并实践练手的项目,欢迎大家关注,一起来学习 Kotlin!