这是之前做直播项目的一个功能,需要实现图片文字混排,并且需要识别出用户名可点击跳转,为了避免页面过深造成一些性能上维护上的问题,于是想到android里的Span来实现这个功能,代码如下,注释很详细就不细说了。
public class SpanTextView extends TextView {
private List<BaseSpanModel> spanModels;
private SpanClickListener listener;
public SpanTextView(Context context) {
super(context);
}
public SpanTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SpanTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 设置文本内容
public void setText(List<BaseSpanModel> spanModels, SpanClickListener listener) {
this.spanModels = spanModels;
this.listener = listener;
this.setClickable(true);
this.setMovementMethod(LinkMovementMethod.getInstance());
//循环取出文本对象
for (int i = 0; i < spanModels.size(); i++) {
BaseSpanModel baseSpanModel = spanModels.get(i);
SpannableString spannableString;
if (baseSpanModel instanceof ClickSpanModel) {
spannableString = getClickableSpan(i, (ClickSpanModel) baseSpanModel);
} else {
spannableString = new SpannableString(baseSpanModel.getContent());
}
// 设置或追加文本内容
if (i == 0) {
this.setText(spannableString);
} else {
this.append(spannableString);
}
}
}
private SpannableString getClickableSpan(int position, ClickSpanModel spanModel) {
SpannableString spannableString = new SpannableString(spanModel.getContent());
int start = 0;
int end = spannableString.length();
//这一行是实现局部点击效果,实现Clickable(自定义的继承ClickableSpan implements OnClickListener)
spannableString.setSpan(new MyClickableSpan(position), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//这一行是设置文字颜色的
spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#fa668e")), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//这一行主要是用来消除点击文字下划线的
spannableString.setSpan(new NoUnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
class MyClickableSpan extends ClickableSpan implements View.OnClickListener {
private int position;
public MyClickableSpan(int position) {
this.position = position;
}
@Override
public void onClick(View v) {
if (listener != null) {
listener.OnClickListener(position);
}
//为了取消点击View效果(某些情况失效)
setText(spanModels, listener);
}
}
public class NoUnderlineSpan extends UnderlineSpan {
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(false);
}
}
public interface SpanClickListener {
void OnClickListener(int position);
}
public static class BaseSpanModel {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public static class ClickSpanModel extends BaseSpanModel {
private int id;
public ClickSpanModel() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
public static class TextSpanModel extends BaseSpanModel {
}
}
组装要显示的文字段落,这里我将响应点击事件的文字封装在ClickSpanModel中,普通文本放在TextSpanModel中。
private List<SpanTextView.BaseSpanModel> createSpanTexts(){
List<SpanTextView.BaseSpanModel> spanModels = new ArrayList<>();
SpanTextView.ClickSpanModel spanModel = new SpanTextView.ClickSpanModel();
spanModel.setContent("Mary");
spanModels.add(spanModel);
SpanTextView.TextSpanModel textSpanModel = new SpanTextView.TextSpanModel();
textSpanModel.setContent(" commented ");
spanModels.add(textSpanModel);
spanModel = new SpanTextView.ClickSpanModel();
spanModel.setContent("Lucy");
spanModels.add(spanModel);
textSpanModel = new SpanTextView.TextSpanModel();
textSpanModel.setContent("'s video,say:Your video is very nice.");
spanModels.add(textSpanModel);
return spanModels;
}
调用方式如下
SpanTextView spannableTextView = (SpanTextView) findViewById(R.id.spanableText);
spannableTextView.setText(createSpanTexts(), new SpanTextView.SpanClickListener() {
@Override
public void OnClickListener(int position) {
ToastUtil.show("You clicked "+createSpanTexts().get(position).getContent()+".");
}
});