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 tags, String content, ClickableSpanNoUnderline.OnClickListener onTagClickListener) {        this.onTagClickListener = onTagClickListener;        StringBuilder sbTag = new StringBuilder();        Map 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 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。

更多相关文章

  1. Android(安卓)使用Shape绘制图形
  2. Android(安卓)vector标签 PathData 画图超详解
  3. Android(安卓)2.3.3 NFC分析
  4. Android帧动画在应用启动时同步启动
  5. android基础控件学习学习(1)【入门篇】
  6. Android延长Toast的时间以及自定义Toast
  7. android 布局 px or dip
  8. Android(安卓)Logcat的使用
  9. Android的logcat输出

随机推荐

  1. Android(安卓)TextView内容过长加省略号,
  2. android的对话框
  3. ActionBar
  4. Android(安卓)activity属性设置大全
  5. Android开发EditText属性
  6. 【Android】Android(安卓)apk默认安装位
  7. android底层开发-android基础架构
  8. Android_传感器综述
  9. Android(安卓)SVG动画PathView源码解析与
  10. Android(安卓)Studio 导出 .aar包的操作