LabelView 是一个标签选择控件。

1. 预览

流式布局 网格布局
线性布局 标签筛选

2. 使用

  1. 在根目录下的 build.gradle 文件中的 repositories 下添加:
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
  1. 添加依赖
dependencies {
    implementation 'com.github.viifo:LabelView:1.0.0'
}
  1. 在 XML 中添加:
<com.viifo.labelview.LabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
  1. 标签监听:
// 设置标签数据
val labels = listOf(...)
labelLayout.setLabelList(labels)
// 标签默认选中    
// labelLayout.setLabelList(labels, defultSelectedLabels)

// 设置标签改变事件监听
labelLayout.setOnItemSelectedChangeListener<String> { selected, status ->
    // selected 为当前使用所有选中的标签
    if (status is LabelChangeStatus.ADD) {
        // todo 标签选中
        // status.item 为选中的标签列表
    } else if (status is LabelChangeStatus.REMOVE) {
        // todo 标签取消选中
        // status.item 为取消选中的标签列表
    } else if (status is LabelChangeStatus.INIT) {
        // todo 标签初始化(默认)选中事件
    }
}
  1. 自定义标签布局:
<com.viifo.labelview.LabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:itemLayout="@layout/label_view_item_with_custom" />
labelLayout.setLabelList(
    data = labels, // 标签数据
    selectedData = null, // 默认选中的标签(可选)
    converter = { holder, item, selected ->
        // todo 自定义标签绑定 view
        // selected - 当前标签是否应该被选中         
    }
)

3. 实现原理

LabelView 控件继承自 RecyclerView, 利用 LinearLayoutManagerGridLayoutManagerFlexboxLayoutManager 分别实现 线性布局网格布局流式布局,关键代码如下所示:

class LabelLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : RecyclerView(context, attrs) {

    /** 标签布局 - 默认为流式布局 */
    private var labelMode: LabelLayout.LabelMode = LabelLayout.LabelMode.FLEX
    /** 标签方向 - 默认为 水平 */ 
    private var orientation: LabelLayout.Orientation = LabelLayout.Orientation.HORIZONTAL
    /** 是否支持滚动 */
    var scrollable: Boolean = false
    /**
     * 流式布局时 item 的对齐方式
     * {@link com.google.android.flexbox.JustifyContent }
     */
    private var justifyContent: Int = 0
    /** 每行显示的 item 个数 */
    private var spanCount: Int = 0

    private fun initLabelLayout(context: Context) {
        layoutManager = when(labelMode) {
            LabelLayout.LabelMode.LINE -> object : LinearLayoutManager(context, orientation.value, false) {
                override fun canScrollHorizontally() = if (orientation == HORIZONTAL) scrollable else false
                override fun canScrollVertically()  = if (orientation == VERTICAL) scrollable else false
            }
            LabelLayout.LabelMode.GRID -> object : GridLayoutManager(context, spanCount) {
                override fun canScrollVertically() = scrollable
            }
            else -> object : FlexboxLayoutManager(context) {
                override fun canScrollVertically() = scrollable
            }.also {
                it.flexDirection = if (orientation == LabelLayout.Orientation.HORIZONTAL) FlexDirection.ROW else FlexDirection.COLUMN
                it.flexWrap = FlexWrap.WRAP
                it.justifyContent = justifyContent
            }
        }
    }

    init {
        // initAttrs(context, attrs)
        initLabelLayout(context)
    }
}

选择了合适和布局管理器,接下来只要给 RecyclerView 设置适配器等一系列操作即可,具体详情请查看源码