安卓富文本编辑器

自动解析超链接和表情。
但写得不是很满意。

public class RichCharSequenceBuilder {

    private CharSequence charSequence;

    private final static String EMPTY_STR = "";
    private final static String HYPERLINK_COLOR = "#5CA2AE";

    public RichCharSequenceBuilder() {
        this.charSequence = EMPTY_STR;
    }

    public RichCharSequenceBuilder(@NonNull CharSequence charSequence) {
        this.charSequence = parse(charSequence);
    }

    public void setCharSequence(@NonNull CharSequence charSequence) {
        this.charSequence = parse(charSequence);
    }

    public CharSequence getCharSequence() {
        return this.charSequence;
    }

    public int length() {
        return charSequence.length();
    }

    @NonNull
    public String toString() {
        return charSequence.toString();
    }

    @NonNull
    public RichCharSequenceBuilder append(CharSequence charSequence) {
        CharSequence temp = parse(charSequence);
        SpannableStringBuilder builder = new SpannableStringBuilder();
        this.charSequence = builder.append(this.charSequence).append(temp);
        return this;
    }

    @NonNull
    public RichCharSequenceBuilder appendImage(int drawableId) {
        this.charSequence = appendImage(this.charSequence, length(), drawableId);
        return this;
    }

    @NonNull
    public RichCharSequenceBuilder appendImage(int index, int drawableId) {
        this.charSequence = appendImage(this.charSequence, index, drawableId);
        return this;
    }

    @NonNull
    public RichCharSequenceBuilder appendImage(@NonNull Bitmap bitmap) {
        this.charSequence = appendImage(this.charSequence, length(), bitmap);
        return this;
    }

    @NonNull
    public RichCharSequenceBuilder appendImage(int index, @NonNull Bitmap bitmap) {
        this.charSequence = appendImage(this.charSequence, index, bitmap);
        return this;
    }

    /**
     * 设定指定字符串为指定颜色
     *
     * @param patternText 如果为空字符串,则改变全文颜色为指定颜色
     * @param color
     * @return
     */
    @NonNull
    public RichCharSequenceBuilder setTextColor(@NonNull String patternText, @NonNull String color) {

        if (TextUtils.isEmpty(charSequence)) {
            return this;
        }

        if (TextUtils.isEmpty(patternText)) {
            SpannableString result = new SpannableString(charSequence);
            result.setSpan(new ForegroundColorSpan(Color.parseColor(color)), 0, result.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            this.charSequence = result;
            return this;
        }

        SpannableStringBuilder builder = new SpannableStringBuilder(charSequence);
        Pattern pattern = Pattern.compile(patternText);
        Matcher matcher = pattern.matcher(builder);
        SpannableStringBuilder result = new SpannableStringBuilder();
        int start = 0;
        while (matcher.find()) {
            int startMatch = matcher.start();
            int endMatch = matcher.end();
            if (startMatch > start) {
                result.append(builder.subSequence(start, startMatch));
            }
            start = endMatch;
            String tempPattern = matcher.group(0);
            SpannableString temp = new SpannableString(tempPattern);
            temp.setSpan(patternText, 0, tempPattern.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            temp.setSpan(new ForegroundColorSpan(Color.parseColor(color)), 0, tempPattern.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            result.append(temp);
        }

        if (start < builder.length()) {
            result.append(builder.subSequence(start, builder.length()));
        }

        this.charSequence = result;
        return this;
    }

    @NonNull
    private CharSequence parse(CharSequence text) {
        if (TextUtils.isEmpty(text)) {
            return EMPTY_STR;
        }
        CharSequence tempText = parseHyperlink(text);
        tempText = parseEmote(tempText);
        return tempText;
    }

    /**
     * 解析超链接
     *
     * @param charSequence
     */
    @NonNull
    private CharSequence parseHyperlink(@NonNull CharSequence charSequence) {
        String temp = charSequence.toString();
        if (!TextUtils.isEmpty(temp)) {
            temp = temp.replace("\n", "<br>");
        }
        Spanned spanned = HtmlCompat.fromHtml(temp, HtmlCompat.FROM_HTML_MODE_LEGACY, null, null);
        SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
        URLSpan[] urls = builder.getSpans(0, spanned.length(), URLSpan.class);
        for (URLSpan span : urls) {
            setLinkClickable(builder, span);
        }

        return builder;
    }

    /**
     * 解析表情
     *
     * @param charSequence
     */
    @NonNull
    private CharSequence parseEmote(@NonNull CharSequence charSequence) {
        return EmoteCenter.getInstance().parseEmote(charSequence);
    }

    private void setLinkClickable(SpannableStringBuilder clickableHtmlBuilder, URLSpan urlSpan) {
        int start = clickableHtmlBuilder.getSpanStart(urlSpan);
        int end = clickableHtmlBuilder.getSpanEnd(urlSpan);
        int flags = clickableHtmlBuilder.getSpanFlags(urlSpan);
        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void onClick(@NonNull View widget) {
                String url = urlSpan.getURL();
                if (!ObjectUtils.isEmpty(url)) {
                    if (url.startsWith("http://") || url.startsWith("https://")) {
                        Uri uri = Uri.parse(url);
                        if (uri != null) {
                            if (AppConfig.isHostLegal(uri.getHost())) {
                                ActivityRouteManager.getInstance().jump(BaseApp.getInstance().getApplicationContext(), "qtpage://web?url=" + URLEncoder.encode(url));
                            }
                        }
                    } else if (url.startsWith("qtpage://")) {
                        ActivityRouteManager.getInstance().jump(BaseApp.getInstance().getApplicationContext(), url);
                    }
                }
            }

            @Override
            public void updateDrawState(@NonNull TextPaint ds) {
                ds.setUnderlineText(true);
                ds.setColor(Color.parseColor(HYPERLINK_COLOR));
            }
        };
        clickableHtmlBuilder.removeSpan(urlSpan);
        clickableHtmlBuilder.setSpan(clickableSpan, start, end, flags);
    }

    /**
     * 添加图片
     *
     * @param text
     * @param index
     * @param image
     * @return
     */
    @NonNull
    private CharSequence appendImage(@NonNull CharSequence text, int index, int image) {
        if (TextUtils.isEmpty(text)) {
            text = EMPTY_STR;
        }

        if (index > text.length()) {
            return text;
        }

        SpannableStringBuilder builder = new SpannableStringBuilder();
        String imageText = "IMG";
        String marginText = " ";
        if (index == 0) {
            builder.append(imageText).append(marginText).append(text);
        } else if (index == text.length()) {
            builder.append(text).append(marginText).append(imageText);
        } else {
            builder.append(text, 0, index).append(marginText)
                    .append(imageText)
                    .append(marginText).append(text, index + 1, text.length());
        }

        VerticalImageSpan span = new VerticalImageSpan(BaseApp.getInstance().getApplicationContext(), image);
        builder.setSpan(span, index, index + imageText.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);

        return builder;
    }

    /**
     * 添加图片
     *
     * @param text
     * @param index
     * @param image
     * @return
     */
    @NonNull
    private CharSequence appendImage(@NonNull CharSequence text, int index, @NonNull Bitmap image) {
        if (TextUtils.isEmpty(text)) {
            text = EMPTY_STR;
        }

        if (index > text.length()) {
            return text;
        }

        SpannableStringBuilder builder = new SpannableStringBuilder();
        String imageText = "IMG";
        String marginText = " ";
        if (index == 0) {
            builder.append(imageText).append(marginText).append(text);
        } else if (index == text.length()) {
            builder.append(text).append(marginText).append(imageText);
        } else {
            builder.append(text, 0, index).append(marginText)
                    .append(imageText)
                    .append(marginText).append(text, index + 1, text.length());
        }

        VerticalImageSpan span = new VerticalImageSpan(BaseApp.getInstance().getApplicationContext(), image);
        builder.setSpan(span, index, index + imageText.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);

        return builder;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值