效果图如上。需求是文本设置最大显示的行数,未超过最大行数时,有多少内容显示多少内容,超过最大行数时只显示最大行数,并在最后一行末尾添加自定义文本如…展开或…点击查看更多等。
实现思路:
- 继承TextView,拿原始要显示的行数和设置的最大行数最对比。
- 运用TextPaint计算出自定义文本的长度。
- 运用Layout和TextUtils得出最后一行要显示的文本和前面行的文本。
- 最后拼接成SpannableStringBuilder设置到TextView。
代码如下
package com.hai.test.widget;
import android.content.Context;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatTextView;
import com.hai.test.R;
/**
* 点击查看更多TextView 借鉴https://www.jianshu.com/p/f4f99eb932d4做了优化
* Created by huanghp on 2019/12/9.
* Email h1132760021@sina.com
*/
public class TailTextView extends AppCompatTextView {
private boolean showTail = true;
private boolean isEllipsed = false;
private static final String LINE_BREAKER = "\n";
private String tailText = "…查看全文";
public TailTextView(Context context) {
this(context, null);
}
public TailTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TailTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//不显示小尾巴或者已经处理过的不再处理
if (!showTail || isEllipsed) {
return;
}
int lineCount = getLineCount();
Layout layout = getLayout();
int maxLines = getMaxLines();
//行数没有到maxLines不处理
if (maxLines == 0 || lineCount < maxLines || TextUtils.isEmpty(getText())) ;
else {
int lineEndIndex = layout.getLineEnd(maxLines - 1);//第maxLines行的首字符offset
int lineStartIndex = layout.getLineStart(maxLines - 1);//第(maxLines - 1)行的首字符offset
if (lineEndIndex >= getText().length()) return;
CharSequence mustShowText = getText().subSequence(0, lineStartIndex);
float tailWidth = getPaint().measureText(tailText);
CharSequence lastLineText;
//最后一个字是个换行符就把这个换行符去掉,不然不能在那一行后面增加文字了
if (LINE_BREAKER.equals(String.valueOf(getText().charAt(lineEndIndex - 1)))) {
lastLineText = getText().subSequence(lineStartIndex, lineEndIndex - 1);
} else {
lastLineText = getText().subSequence(lineStartIndex, lineEndIndex);
}
int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
CharSequence ellipsizeLastLineText = TextUtils.ellipsize(lastLineText, getPaint(), availableWidth - tailWidth,
TextUtils.TruncateAt.END);
if (ellipsizeLastLineText.length() > 2 && ellipsizeLastLineText != lastLineText) {
lastLineText = ellipsizeLastLineText.subSequence(0, ellipsizeLastLineText.length() - 1);
}
SpannableStringBuilder ssb = new SpannableStringBuilder(mustShowText);
ssb.append(lastLineText).append(tailText);
ssb.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorAccent)), ssb.length() - tailText.length(), ssb.length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
setText(ssb);
//重置一下这个位
isEllipsed = false;
}
}
}
重要的地方都有注释,相信大家一看就明白。
说明一下,大家拿到他还可以进行扩展,比如通过自定义属性设置自定义文本、设置自定义文本的颜色以及自定义文本的点击事件,让用起来更方便。