48.Android 标签TextView的点击技巧
- Android 标签TextView的点击技巧
- 前言
- ClickableSpan源码
- 自定义ClickableSpan
- TagTextView
- TagTextViewActivity
- 效果图
- github
前言
在一些圈子性质的页面里,每条动态的文本往往都是富文本。
其中就有一种掺杂了标签的富文本内容。如新浪微博的标签富文本。
……
……
而且,最重要的是:这些标签可以点击。
这涉及到ClickableSpan的使用。
ClickableSpan源码
/** * If an object of this type is attached to the text of a TextView * with a movement method of LinkMovementMethod, the affected spans of * text can be selected. If clicked, the {@link #onClick} method will * be called. */public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance { /** * Performs the click action associated with this span. */ public abstract void onClick(View widget); /** * Makes the text underlined and in the link color. */ @Override public void updateDrawState(TextPaint ds) { ds.setColor(ds.linkColor); ds.setUnderlineText(true); }}
ClickableSpan源码很短,主要是拓展了CharacterStyle的updateDrawState功能,和对外提供了一个onClick方法。
我们可以看到ClickableSpan字体的颜色跟随着linkColor的颜色,默认是有下划线的。
但是新浪微博的那个标签是没有下划线的,颜色也最好是可以自定义的。
以上得出:需要自定义一个ClickableSpan。
自定义ClickableSpan
public class ClickableSpanNoUnderline extends ClickableSpan { private static final String TAG = "ClickableSpan"; private static final int NO_COLOR = -206; private int color; private OnClickListener onClickListener; public ClickableSpanNoUnderline(int color, OnClickListener onClickListener) { super(); this.color = color; this.onClickListener = onClickListener; } public ClickableSpanNoUnderline(OnClickListener onClickListener) { this(NO_COLOR, onClickListener); } /** * Makes the text underlined and in the link color. * * @param ds */ @Override public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); // 设置文字颜色 if (this.color == NO_COLOR) { ds.setColor(ds.linkColor); } else { ds.setColor(this.color); } ds.clearShadowLayer(); // 去除下划线 ds.setUnderlineText(false); ds.bgColor = Color.TRANSPARENT; } /** * Performs the click action associated with this span. * * @param widget widget */ @Override public void onClick(View widget) { if (this.onClickListener != null) { this.onClickListener.onClick(widget, this); } else { Log.w(TAG, "listener was null"); } } /** * 回调接口,回调自身的onClick事件 * 告诉外部 是否被点击 */ public interface OnClickListener<T extends ClickableSpanNoUnderline> { /** * ClickableSpan被点击 * * @param widget widget * @param span span */ void onClick(View widget, T span); }}
updateDrawState()
方法就是设置了颜色和没有下划线。
OnClickListener:定义了一个泛型回调接口,方便你拓展自己的ClickableSpanNoUnderline:如果你的标签需要保存一些Id或者Content内容等等,你可以继承ClickableSpanNoUnderline,实现你自定义的ClickableSpanNoUnderline,然后在View层回调的时候实现ClickableSpanNoUnderline.OnClickListener<你自定义的ClickableSpanNoUnderline>。(模糊的话,可以看底下的 TagTextView 和 TagTextViewActivity)
TagTextView
这里就随便写一个自定义TextView,将刚才的ClickableSpanNoUnderline用上。
public class TagTextView extends TextView { private ClickableSpanNoUnderline.OnClickListener onTagClickListener; public TagTextView(Context context) { super(context); } public TagTextView(Context context, AttributeSet attrs) { super(context, attrs); } public TagTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public TagTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } /** * 添加标签ClickableSpan * * @param tags tags * @param content content * @return SpannableStringBuilder */ public SpannableStringBuilder addTagClickableSpan(ArrayList<Tag> tags, String content, ClickableSpanNoUnderline.OnClickListener onTagClickListener) { this.onTagClickListener = onTagClickListener; StringBuilder sbTag = new StringBuilder(); Map<String, Tag> content2TagDict = new HashMap<>(); /** * 添加 # */ if (tags != null && tags.size() > 0) { for (Tag tag : tags) { sbTag.append("#"); sbTag.append(tag.getContent()); sbTag.append("#"); sbTag.append(" "); content2TagDict.put(tag.getContent(), tag); } } int tagLength = sbTag.toString().length(); sbTag.append(content); /** * 添加颜色 */ SpannableStringBuilder sb = new SpannableStringBuilder(sbTag.toString()); if (tagLength > 0) { String s = sb.toString(); String[] model = s.split("#"); for (int i = 0; i < model.length - 1; i++) { /** * 过滤 "" 和 " " */ if ("".equals(model[i]) || " ".equals(model[i])) continue; int index = s.indexOf(model[i]); int mLength = model[i].length(); TagClickableSpan span = new TagClickableSpan(0xffFF4081, this.onTagClickListener); span.setContent(model[i]); Tag tag = content2TagDict.get(model[i]); if (tag != null && tag.getId() != null) { span.setId(tag.getId()); } /** * 设置TagClickableSpan */ sb.setSpan(span, index - 1, index + mLength + 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE); } } return sb; } /** * Tag ClickableSpan */ public class TagClickableSpan extends ClickableSpanNoUnderline { private Long id; private String content; public TagClickableSpan(int color, OnClickListener onClickListener) { super(color, onClickListener); } public TagClickableSpan(OnClickListener onClickListener) { super(onClickListener); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }}
TagTextViewActivity
根据我上面写的TagTextView,这里要实现ClickableSpanNoUnderline.OnClickListener
public class TagTextViewActivity extends AppCompatActivity implements ClickableSpanNoUnderline.OnClickListener<TagTextView.TagClickableSpan> { private TagTextView tagTV; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.activity_tag_textview); this.tagTV = (TagTextView) this.findViewById(R.id.tag_text_view_tv); this.initData(); } private void initData() { ArrayList<Tag> tags = new ArrayList<>(); Tag tag1 = new Tag(); tag1.setId(2601L); tag1.setContent("初心不改"); Tag tag2 = new Tag(); tag2.setId(2602L); tag2.setContent("方能始终"); Tag tag3 = new Tag(); tag3.setId(2603L); tag3.setContent("Save You From Anything"); tags.add(tag1); tags.add(tag2); tags.add(tag3); String sign = "这个世上不存在束缚人的枷锁......Save You From Anything......"; this.tagTV.setText(this.tagTV.addTagClickableSpan(tags, sign, this)); // 在单击链接时凡是有要执行的动作,都必须设置MovementMethod对象 this.tagTV.setMovementMethod(LinkMovementMethod.getInstance()); // 设置点击后的颜色,这里涉及到ClickableSpan的点击背景 this.tagTV.setHighlightColor(0xff8FABCC); } /** * ClickableSpan被点击 * * @param widget widget * @param span span */ @Override public void onClick(View widget, TagTextView.TagClickableSpan span) { ToastUtil.show(this, span.getId() + ":" + span.getContent(), Toast.LENGTH_SHORT); }}
效果图
github
觉得代码不全的,可以去github里的NO.31。当然也求Star,T T。
- 2楼kristch_wu昨天 19:06
- 不错 学习了
- Re: qq_16430735昨天 20:48
- 回复kristch_wunthanks. :)
- 1楼u010850027昨天 14:19
- Save you from anything
- Re: qq_16430735昨天 17:23
- 回复u010850027n:)