效果

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
}
})
如果本文对你有帮助就点个赞支持下吧~~~
网友评论