美文网首页android
Android自定义View之展开/收缩热门搜索关键词控件

Android自定义View之展开/收缩热门搜索关键词控件

作者: 寻水的鱼Chock | 来源:发表于2023-07-30 17:32 被阅读0次

效果

展开收缩.png

Tips:
1、文字长度任意,一行显示不下自适应换行,不裁剪不压缩不遮挡;
2、文字UI效果支持自定义(颜色/大小/背景/内边距/外边距等);
3、"折叠"、"展开"按钮UI效果支持自定义(可图片/可文字/背景/内边距/外边距等);
4、折叠、展开时行数限制支持设置(超过x行显示"展开",展开不超过y行);
5、起始行数不够x行时"展开"按钮不显示;
6、"折叠"按钮可不显示;

源码

/**
 * //依赖Flex Box
 * api 'com.google.android:flexbox:2.0.1'
 */
class MoreTextItemLayout : FrameLayout {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes)

    private val mFlexboxLayout by lazy {
        val x = FlexboxLayout(context)
        addView(x)
        x.layoutParams?.let {
            it.width = ViewGroup.LayoutParams.MATCH_PARENT
            it.height = ViewGroup.LayoutParams.WRAP_CONTENT
        }
        x.flexWrap = FlexWrap.WRAP
        x.alignContent = AlignContent.FLEX_START
        x.alignItems = AlignItems.CENTER
        x.flexDirection = FlexDirection.ROW
        //x.background = ColorDrawable(Color.BLUE)
        x
    }

    var data = mutableListOf<MoreTextItem>()
        private set

    private var mMoreTextItemListener: MoreTextItemListener? = null
    var moreLines = 3
    var moreMaxLines = 8

    /**
     * 0:折叠模式  1: 展开模式
     */
    var currentMode = 0
        set(value) {
            field = value
            refreshViews()
        }

    private val expandView by lazy {
        mMoreTextItemListener?.onCreateMoreActionView(false)
    }

    private val foldView by lazy {
        mMoreTextItemListener?.onCreateMoreActionView(true)
    }

    private val flexBoxWidth
        get() = width - paddingStart - paddingEnd

    private val mCurrentModeLines
        get() = if (currentMode == 0) moreLines else moreMaxLines

    @UiThread
    fun setData(list: List<MoreTextItem>, listener: MoreTextItemListener?) {
        data.clear()
        data.addAll(list)
        mMoreTextItemListener = listener
        refreshViews()
    }

    private fun refreshViews() {
        if (width == 0) {
            post {
                layoutViews()
            }
        } else {
            layoutViews()
        }
    }

    private fun layoutViews() {
        val list = ArrayList(data)
        val size = list.size
        "list.size=${list.size}".debugLog()
        val cv = mFlexboxLayout
        while (cv.childCount > size) {
            cv.removeViewAt(size)
        }
        for (index in 0 until size) {
            val s = list[index]
            val c = cv.getChildAt(index) as? TextView
            if (c != null) {
                c.text = s.text
                mMoreTextItemListener?.onCustomViewApply(c, index)
                c.setOnClickListener {
                    mMoreTextItemListener?.onItemClick(it, index)
                }
            } else {
                val cc = TextView(context)
                cv.addView(cc)
                cc.text = s.text
                mMoreTextItemListener?.onCustomViewApply(cc, index)
                cc.setOnClickListener {
                    mMoreTextItemListener?.onItemClick(it, index)
                }
            }
        }
        cv.measure(
            MeasureSpec.makeMeasureSpec(flexBoxWidth, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.AT_MOST)
        )
        val ls = cv.flexLines
        "mCurrentModeLines=$mCurrentModeLines lines=${ls.size}".debugLog()
        if (ls.size >= mCurrentModeLines) {
            var x = 0
            for (i in mCurrentModeLines until ls.size) {
                x += ls[i].itemCount
                //"flexLines itemCount x = ${x}".debugLog("@@")
            }
            val s = list.size - x
            repeat(x) {
                cv.removeViewAt(s)
            }
            addActionView(s)
        } else {
            if (currentMode != 0 && ls.size >= moreLines) {
                addActionView(list.size)
            }
        }
    }

    private fun addActionView(s: Int) {
        if (mMoreTextItemListener != null) {
            val cv = mFlexboxLayout
            val cm = currentMode != 0
            val v = if (cm) foldView else expandView
            if (v != null) {
                cv.removeView(v)
                cv.addView(v)
                v.layoutParams?.let {
                    it.width = -2
                    it.height = -2
                }
                v.setOnClickListener {
                    cv.removeView(it)
                    currentMode = if (cm) 0 else 1
                    layoutViews()
                }
                var more = 1
                while (true) {
                    cv.measure(
                        MeasureSpec.makeMeasureSpec(flexBoxWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.AT_MOST)
                    )
                    val ls = cv.flexLines
                    "action => flexLines=${ls.size} lines=${mCurrentModeLines}".debugLog("@@")
                    if (ls.size > mCurrentModeLines) {
                        cv.removeViewAt(s - more)
                        more++
                    } else {
                        break
                    }
                }
            }
        }
    }

    var debug = false

    private inline fun String.debugLog(tag: String = "@@") {
        if (debug) {
            Log.d(tag, this)
        }
    }

}

open class MoreTextItem {
    var text: String? = null
    var extra: Any? = null
}

interface MoreTextItemListener {

    /**
     * View点击事件
     * @param v 被点击View
     * @param position 索引位置
     */
    fun onItemClick(v: View, position: Int)

    /**
     * 需要对View自定义布局
     * @param v
     * @param position 位置
     */
    fun onCustomViewApply(v: TextView?, position: Int)

    /**
     * 创建展开收缩布局
     * @param expand true 展开 false 收缩
     * @return 任意布局视图
     */
    fun onCreateMoreActionView(expand: Boolean): View?

}

简单使用

1)添加依赖
//Flex Box
implementation 'com.google.android:flexbox:2.0.1'
2)自定义UI效果
val ts = arrayListOf(
    "互发",
    "村",
    "春潮",
    "12342424",
    "发改委:全面落实带薪休假制度热",
    "全面落实",
    "带薪休假制度",
    "热点",
    "冰点1234",
    "带薪休假",
    "非常",
    "带薪",
    "休假",
    "落实带薪",
    "全面落实",
    "洪水过后女子家中密密麻麻全是鱼",
    "洪水过后",
    "家中",
    "密密麻麻",
    "全是鱼",
    "王健林动荡30天",
    "30天",
    "动荡",
)
val mMoreTextItem = mutableListOf<MoreTextItem>()
ts.forEach {
    mMoreTextItem.add(MoreTextItem().apply {
        text = it
    })
}
binding.mtil.setData(mMoreTextItem, object : MoreTextItemListener {
    override fun onItemClick(v: View, position: Int) {
        Toast.makeText(
            this@MainActivity,
            "Click ${mMoreTextItem.get(position).text}",
            Toast.LENGTH_SHORT
        ).show()
    }

    override fun onCustomViewApply(v: TextView?, position: Int) {
        v?.setPadding(16.dpToPx(), 5.dpToPx(), 16.dpToPx(), 5.dpToPx())
        v?.setBackgroundResource(R.drawable.shape_f7f7f7_15)
        v?.setTextColor(Color.parseColor("#333333"))
        (v?.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
            it.width = -2
            it.height = -2
            it.topMargin = 5.dpToPx()
            it.bottomMargin = 5.dpToPx()
            it.leftMargin = 4.dpToPx()
            it.rightMargin = 4.dpToPx()
            v.layoutParams = it
        }
    }

    override fun onCreateMoreActionView(expand: Boolean): View? {
        val ly = LinearLayout(this@MainActivity)
        ly.orientation = LinearLayout.HORIZONTAL
        ly.gravity = Gravity.CENTER
        val v = TextView(this@MainActivity)
        v.setBackgroundResource(R.drawable.shape_f7f7f7_15)
        v.text = if (expand) "收缩" else "展开"
        v.gravity = Gravity.CENTER
        v.setTextColor(Color.parseColor("#FF0000"))
        v.setPadding(16.dpToPx(), 5.dpToPx(), 16.dpToPx(), 5.dpToPx())
        ly.addView(v)
        (v.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
            it.width = -2
            it.height = -2
            it.topMargin = 5.dpToPx()
            it.bottomMargin = 5.dpToPx()
            it.leftMargin = 4.dpToPx()
            it.rightMargin = 4.dpToPx()
            v.layoutParams = it
        }
        return ly
    }
})



如果本文对你有帮助就点个赞支持下吧~~~

相关文章

网友评论

    本文标题:Android自定义View之展开/收缩热门搜索关键词控件

    本文链接:https://www.haomeiwen.com/subject/hbuspdtx.html