在开发中通常通常会遇到图文混编的情况,例如文字中插入置顶,热门等图片标签,在很多时候换行的时候TextView文字换行后都无法占满首位字符,要求TextView中要求某些文字设置其他颜色,字体,样式等等,这篇文章就是解决这种问题,如下:
一、SpannableString
SpannableString是富文本显示效果一个重要的类,是CharSequence的一种,和String一样,都是一种字符串类型,SpannableString可以直接作为TextView的显示文本,不同的是SpannableString可以通过使用其方法setSpan方法实现字符串各种形式风格的显示,重要的是可以指定设置的区间,也就是为字符串指定下标区间内的子字符串设置格式。在开发中,TextView可以通过setText(CharSequence)传入SpannableString作为参数,来达到显示不同样式文字的效果。一般通过以下方式进行设置:
spannableString.setSpan(Object what, int start, int end, int flags);
- what:对SpannableString进行处理的各种Span;
- int:需要处理文字段开始的下标;
- end:需要处理文字段结束的下标;
- flags:决定开始和结束下标是否包含的标志位,有四个参数可选
- SPAN_INCLUSIVE_EXCLUSIVE:包括开始下标,但不包括结束下标
- SPAN_EXCLUSIVE_INCLUSIVE:不包括开始下标,但包括结束下标
- SPAN_INCLUSIVE_INCLUSIVE:既包括开始下标,又包括结束下标
- SPAN_EXCLUSIVE_EXCLUSIVE:不包括开始下标,也不包括结束下标
除了SpannableString之外还有SpannableStringBuilder和SpannableString相似,类似于String和SpannableStringBuilder的关系, 在实际开发中发现SpannableStringBuilder字符占位会出现问题,所有没有用。
二、setSpan
在这里主要介绍两类Span:ClickableSpan和ImageSpan,在开发中基本够用了。
1、ClickableSpan 继承自CharacterStyle,用来设置指定区间文字的点击事件和文字样式设置。
class TopicSpan extends ClickableSpan {
@Override
public void onClick(@NonNull View widget) {
Toast.makeText(context,"点击了处理的文字",Toast.LENGTH_LONG).show();
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
//设置颜色、字体大小等等
ds.setColor(Color.parseColor("#ff000"));
//设置取消下划线
ds.setUnderlineText(false);
}
}
//在这里可以处理表情库添加到SpannableString构造方法里面作为参数
SpannableString spannable = new SpannableString(text);
spannable.setSpan(new TopicSpan(), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textview.setText(spannable);
//如果处理的本文需要点击跳转,则需要加上setMovementMethod响应点击事件,否则点击无响应
textview.setMovementMethod(LinkMovementMethod.getInstance());
2、ImageSpan:继承自DynamicDrawableSpan,用在在指定的文本中插入图片。
class LogoSpan extends ImageSpan {
public LogoSpan(@NonNull Context context, @NonNull Bitmap b) {
super(context, b);
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,
@NonNull Paint paint) {
//处理让图片和文字居中,不处理会对不齐
Drawable b = getDrawable();
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int transY = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2;//计算y方向的位移
canvas.save();
canvas.translate(x, transY);//绘制图片位移一段距离
b.draw(canvas);
canvas.restore();
}
}
//" "表示图片插入的文本
SpannableString imageSpannable = new SpannableString(" ");
LogoSpan imageSpan = new LogoSpan(context, bitmap);
//0, 1表示插入的文本位置
imageSpannable.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textview.setText(imageSpannable);
三、文本超过几行显示省略号
设置SpannableString后的TextView设置android:ellipsize="end"是无法生效的,但是android:maxLines="4"是可以生效的,意味着超过4行是无法显示省略号,直接就被截断了,需要重写TextVie的onDraw方法:
private static final String YX_THREE_DOTS = "...";
private static final int YX_THREE_DOTS_LENGTH = YX_THREE_DOTS.length();
private SpannableStringBuilder mSpannableStringBuilder;
@Override
protected void onDraw(Canvas canvas) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
final Layout layout = getLayout();
if (layout.getLineCount() >= getMaxLines()) {
CharSequence charSequence = getText();
int lastCharDown = layout.getLineVisibleEnd(getMaxLines() - 1);
if (lastCharDown >= YX_THREE_DOTS_LENGTH && charSequence.length() > lastCharDown) {
if (mSpannableStringBuilder == null) {
mSpannableStringBuilder = new SpannableStringBuilder();
} else {
mSpannableStringBuilder.clear();
}
mSpannableStringBuilder.append(charSequence.subSequence(0, lastCharDown - 1))
.append(YX_THREE_DOTS);
setText(mSpannableStringBuilder);
}
}
}
super.onDraw(canvas);
}