48.Android 标签TextView的点击技巧

来源:互联网 发布:excel数据有效性空格 编辑:程序博客网 时间:2024/04/30 15:03

48.Android 标签TextView的点击技巧

  • Android 标签TextView的点击技巧
    • 前言
    • ClickableSpan源码
    • 自定义ClickableSpan
    • TagTextView
    • TagTextViewActivity
    • 效果图
    • github


前言

在一些圈子性质的页面里,每条动态的文本往往都是富文本

其中就有一种掺杂了标签的富文本内容。如新浪微博的标签富文本

tag_textview_1

……
……
而且,最重要的是:这些标签可以点击

tag_textview_2

这涉及到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);    }}

效果图

tag_textview_3


github

觉得代码不全的,可以去github里的NO.31。当然也求Star,T T。

3 0