前言
下左是UI给的效果图,最初准备用Manabu-GT/ExpandableTextView去实现,如下第二、三张图效果尚可,但是跟实际UI略有出入:
- 折叠后的最后一行没有省略号。
- 折叠箭头在文本最下面,而不是在最后一行的最右边。
为了解决这个问题,就只能自己动手撸一个。
效果图
话不多说,上效果图。跟UI设计图 基本完全一致。
实现思路
- 首先需要计算文本在TextView完全显示的最大行数。
- 然后需要获取文本在TextView中第n行的内容,并且可以设置初始显示行数m。
- 看下图。
核心代码
计算行数
/**
* 计算行数
*/
private fun initLines() {
//将文本内容全部给tvFirst,方便后面计算行数,获取每行的文本内容
tvFirst.text = text
tvFirst.post {
//绘制时获取tvFirst实际总行数,直接获取返回的是0
maxLines = tvFirst.lineCount
//最大行数小于等于要显示的行数
if (maxLines <= showLines) {
//tvSecond隐藏,只显示tvFirst
tvSecond.visibility = View.GONE
tvFirst.setLines(tvFirst.lineCount)
} else {
tvSecond.visibility = View.VISIBLE
//获取tvFirst显示布局,可根据改布局获取文本中每行的开始位置和结束位置
val layout = tvFirst.layout
if (isExpand) {//展开
//获取展开最后一行的内容,展开时给tvSecond显示
lastLineStr = text.substring(layout.getLineStart(maxLines - 1), layout.getLineEnd(maxLines - 1))
//设置tvFirst的行数=最大行数-1
tvFirst.setLines(maxLines - 1)
//设置tvSecond的行数控制其显示内容,此处设置为maxLines=2是因为可能存在:
//最后一行的内容刚好完全填满,而tvSecond由于右边drawableRight和drawablePadding的存在所以可能显示不全,只能加多一行显示。
tvSecond.maxLines = 2
tvSecond.text = lastLineStr
} else {
//设置tvFirst的行数=折叠时显示的行数-1
tvFirst.setLines(showLines - 1)
val start = layout.getLineStart(showLines - 1)
val end = layout.getLineEnd(showLines - 1)
secondTextLineStr = text.substring(start, end)
//获取折叠时最后一行的内容,给tvSecond显示
tvSecond.maxLines = 1
tvSecond.text = secondTextLineStr
}
}
}
}
处理列表展开错乱的情况
/**
* 在列表中使用赋值文本请使用此方法
* 用SparseBooleanArray记录列表中展开的位置,防止错乱
*/
fun setText(string: String, collapsedStatus: SparseBooleanArray, position: Int) {
mCollapsedStatus = collapsedStatus
mPosition = position
val isCollapsed = collapsedStatus[position, false]
this.setText(string, isCollapsed)
}
使用
XML
<com.demon.expandablelibrary.ExpandableTextView
android:id="@+id/expandTextView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:padding="30dp"
app:isExpand="true"
app:showLines="4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/expandTextView1"
app:textColor="@color/colorPrimary"
app:textSize="15sp" />
基本赋值
expandTextView.setText("新西兰总理杰辛达阿德恩(Jacinda Ardern)表示,新西兰的华人社区
是新西兰历史最悠久、规模最大的社区之一。")
在列表中使用
class Adapter(val context: Context) : RecyclerView.Adapter<Adapter.Holder>() {
private val mCollapsedStatus: SparseBooleanArray = SparseBooleanArray()
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val expandText = itemView.findViewById<ExpandableTextView>(R.id.expandText)
val tvNo = itemView.findViewById<TextView>(R.id.tvNo)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list, parent, false)
return Holder(view)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.run {
tvNo.text = "$position"
expandText.setText(context.getString(R.string.long_text), mCollapsedStatus, position)
}
}
override fun getItemCount(): Int = 20
}
自定义属性
属性 | 说明 |
---|---|
text | 文本内容 |
textColor | 字体颜色 |
textSize | 字体大小 |
expandDrawable | 向下展开时的图标 |
collapseDrawable | 向上折叠时的图标 |
showLines | 折叠时显示的文本行数,默认3 |
isExpand | 初始是否展开,默认false |
更多
Demo和源码请看:
GitHub:https://github.com/DeMonLiu623/ExpandableTextView