小需求:默认展示4行,超过4行出现“查看全部”,点击则加载浮层显示完整内容;
实现是参考下面网址代码,但是有一些改动(主要涉及纯引英文的文本下面网址的代码可能有些问题)
https://www.jianshu.com/p/f4f99eb932d4
最终显示代码如下:
import android.content.Context
import android.graphics.Color
import android.support.v7.widget.AppCompatTextView
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.TextPaint
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.util.AttributeSet
import android.view.View
import android.widget.Toast
/**
* @date: 2020.02.25
*/
class FoldTextView : AppCompatTextView {
private var isSupportFold = false
private var clickCallback: TextClickCallback? = null
private var isNeedEllipsis = true
constructor(context: Context) : this(context, null) {
}
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.PayFoldTextView)
isSupportFold = ta.getBoolean(R.styleable.FoldTextView_supportFold, false)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (isSupportFold) {
//获取当前的行数
val lineCount = lineCount
val layout = layout
val maxLines = maxLines
if (maxLines == 0 || lineCount < maxLines || TextUtils.isEmpty(text)) {
return
}
val totalChars = layout.getLineEnd(maxLines - 1)
val lastLineStartIndex = layout.getLineStart(maxLines - 1)
if (totalChars >= text.length) {
return
}
val mustShowText = text.subSequence(0, lastLineStartIndex)
val tailWidth = paint.measureText(TAIL_TEXT)
var lastLineText: CharSequence
if (LINE_BREAKER == text[totalChars - 1].toString()) {
lastLineText = text.subSequence(lastLineStartIndex, totalChars - 1)
} else {
lastLineText = text.subSequence(lastLineStartIndex, totalChars)
}
val maxWidth = measuredWidth.toFloat()
val ellipsizeLastLineText = TextUtils.ellipsize(
lastLineText, paint, maxWidth - tailWidth,
TextUtils.TruncateAt.END
)
if (ellipsizeLastLineText.length > 2 && ellipsizeLastLineText != lastLineText) {
lastLineText = ellipsizeLastLineText.subSequence(0, ellipsizeLastLineText.length - 1)
isNeedEllipsis = true
} else {
isNeedEllipsis = false
}
val spannableStringBuilder = SpannableStringBuilder(mustShowText)
if (!mustShowText.endsWith("\n")) {
spannableStringBuilder.append(LINE_BREAKER)
}
spannableStringBuilder.append(lastLineText)
if (isNeedEllipsis) {
spannableStringBuilder.append("...")
spannableStringBuilder.append(TAIL_SEPARATOR)
} else {
spannableStringBuilder.append(TAIL_SEPARATOR_O)
}
spannableStringBuilder.append(buildClickText())
//重新设置文本
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT //设置点击后的颜色为透明
super.setText(spannableStringBuilder)
}
}
companion object {
val LINE_BREAKER = "\n"
val TAIL_TEXT = "...查看全部"
val TAIL_SEPARATOR = " "
val TAIL_SEPARATOR_O = " "
val TAIL_TEXT_WITHOUT_ELLIPSIS = "查看全部"
}
fun setClickCallback(clickCallback: TextClickCallback?) {
this.clickCallback = clickCallback
}
private fun buildClickText(): SpannableStringBuilder {
val spannableString =
SpannableStringBuilder(TAIL_TEXT_WITHOUT_ELLIPSIS)
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
clickCallback?.respondClick()
}
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = resources.getColor(R.color.pay_color_0086f6)
ds.isUnderlineText = false
}
}
spannableString.setSpan(clickableSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannableString
}
fun setSupportFold(isSupport: Boolean = false) {
isSupportFold = isSupport
maxLines = Integer.MAX_VALUE
}
}
<declare-styleable name="FoldTextView">
<attr name="supportFold" format="boolean"/>
</declare-styleable>
相关注释:
supportFold:是否需要支持文本折叠。
buildClickText()方法主要是给查看全部添加点击事件以及相关样式。
下面代码是:指定Paint属性的宽度下的文本是否与原始文本相同,不相同则需要显示省略号及查看全部。
if (ellipsizeLastLineText.length > 2 && ellipsizeLastLineText != lastLineText) {
lastLineText = ellipsizeLastLineText.subSequence(0, ellipsizeLastLineText.length - 1)
isNeedEllipsis = true
} else {
isNeedEllipsis = false
}
ellipsize方法需要的相关参数:
text:原始的文本内容 paint:canvas用到的画笔 avail:画布能提供的宽度 where:枚举类型,就是 省略号显示的位置
/**
* Returns the original text if it fits in the specified width
* given the properties of the specified Paint,
* or, if it does not fit, a truncated
* copy with ellipsis character added at the specified edge or center.
*/
public static CharSequence ellipsize(CharSequence text,
TextPaint p,
float avail, TruncateAt where) {
return ellipsize(text, p, avail, where, false, null);
}
下面的代码加上"\n"是因为在英文条件下,最后一行的文字可能会显示到倒数第二行,所以强制加上换行符。
val spannableStringBuilder = SpannableStringBuilder(mustShowText)
if (!mustShowText.endsWith("\n")) {
spannableStringBuilder.append(LINE_BREAKER)
}