Android 使用Span富文本处理Html标签

(写在题前,Html.fromHtml()方法也可以实现处理html标签的功能,但一般适用于最简简单单的显示文本内容的功能需求,如果TextView本身有其他效果需要自定义且已经用到了Span进行处理或是效果与文本内容本身的实际有效长度有关(标签本身其实不是有效内容),可能用Html.fromHtml()就有很大限制了。

其实就是使用Span富文本处理html标签更加灵活。)


先说结论,直接上代码。
String title="xxxxxxx";//其中含有<sub></sub>等标签
tv.setText(getTabSpan(title,tv.getPaint());
/**
 * 处理标签<sup></sup>等
 * @param title
 * @param textPaint
 * @return
 */
public static SpannableStringBuilder getTabSpan(String title, TextPaint textPaint) {

    SpannableStringBuilder spanBuilder=new SpannableStringBuilder(title);
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"<sup>","</sup>");
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"<sub>","</sub>");
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"<strong>","</strong>");
    //期待更多支持的标签。。。
    return spanBuilder;
}
private static SpannableStringBuilder getHtmlSpan(SpannableStringBuilder spanBuilder, TextPaint textPaint,String pre,String suffix) {
    int fontSizeSp=(int)(textPaint.getTextSize()/textPaint.density);
    String key=pre+"(.+?)"+suffix;
    CharacterStyle span;
    Pattern p;
    int preLength=pre.length();
    int suffixLength=suffix.length();
    String title=spanBuilder.toString();
    try {
        p = Pattern.compile(key);
        Matcher m = p.matcher(title);
        //LogSuperUtil.i("cnki_wiki_data","fontSizeSp="+fontSizeSp);
        while (m.find()) {
            int startIndex=m.start();
            int endIndex=m.end();//是表达式结束的位置,并不是后缀开始的位置。
            int spanStartIndex=startIndex+preLength;
            int spanEndIndex=endIndex-suffixLength;
            if("<sup>".equals(pre)) {
                span = new SuperscriptSpan();// 需要重复!
                spanBuilder.setSpan(span,spanStartIndex,spanEndIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                AbsoluteSizeSpan sizeSpan=new AbsoluteSizeSpan(fontSizeSp,false);
                spanBuilder.setSpan(sizeSpan,spanStartIndex,spanEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }else if("<sup>".equals(pre)) {
                span = new SuperscriptSpan();// 需要重复!
                spanBuilder.setSpan(span,spanStartIndex,spanEndIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                AbsoluteSizeSpan sizeSpan=new AbsoluteSizeSpan(fontSizeSp,false);
                spanBuilder.setSpan(sizeSpan,spanStartIndex,spanEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }else if("<strong>".equals(pre)) {
                span = new StyleSpan(android.graphics.Typeface.BOLD);// 需要重复!
                spanBuilder.setSpan(span,spanStartIndex,spanEndIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
            //LogSuperUtil.i("cnki_wiki_data","上标 start="+start+",end="+end);*/
            spanBuilder.delete(endIndex-suffixLength,endIndex);
            spanBuilder.delete(startIndex,startIndex+preLength);
            title=spanBuilder.toString();
            m=p.matcher(title);
        }

    } catch (Exception e) {
        p = Pattern.compile("[^0-9]");
    }
    return spanBuilder;
}

最重要的是架构设计,是思想,当然最最重要的是技术关键点。

从踩过的坑中去总结,重要的几个知识点要知道。

1.SpannableStringBuilder的delete方法不会影响到本身已设置的富文本效果,索引不会自动移位。这保证了对<sup>15</sup>N度这种文本处理后,删除“<sup>”和“</sup>”字符串字样不会影响“15”字样的上标效果。

2.SpannableStringBuilder spanBuilder=new SpannableStringBuilder(charSq);

如果charSq已经是个富文本,那么spanBuilder不会破坏已有的富文本效果。

当然,在本例中,在1的基础上,2的用途可能就用不上了。因为拿到SpannableStringBuilder的实例引用,就可以多次处理想要的富文本效果。

另外,在实现关键字处理高亮显示标红的效果时,也对正则表达式的应用有一点总结。

代码是这样的:

private  SpannableStringBuilder getKeywordSpan(SpannableStringBuilder spanBuilder) {
    String pre="###";
    String suffix="\\$\\$\\$";
    String key=pre+"(.+?)"+suffix;
    CharacterStyle span;
    Pattern p;
    int preLength=pre.length();
    int suffixLength=suffix.length();
    suffixLength=3;//强制
    String title=spanBuilder.toString();
    try {
        p = Pattern.compile(key);
        Matcher m = p.matcher(title);
        int color=mContext.getResources().getColor(R.color.red);
        //LogSuperUtil.i("cnki_wiki_data","fontSizeSp="+fontSizeSp);
        while (m.find()) {
            int startIndex=m.start();
            int endIndex=m.end();//是表达式结束的位置,并不是后缀开始的位置。
            int spanStartIndex=startIndex+preLength;
            int spanEndIndex=endIndex-suffixLength;
            span = new ForegroundColorSpan(color);// 需要重复!
            spanBuilder.setSpan(span,spanStartIndex,spanEndIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            //LogSuperUtil.i("cnki_wiki_data","上标 start="+start+",end="+end);*/
            spanBuilder.delete(spanEndIndex,endIndex);
            spanBuilder.delete(startIndex,spanStartIndex);
            title=spanBuilder.toString();
            m=p.matcher(title);
        }

    } catch (Exception e) {
        p = Pattern.compile("[^0-9]");
    }
    return spanBuilder;
}

其实就是匹配“###xx$$$”这个,由于$这个符号在正则中表示表达式结束,那么需要转义,\$才是表示的$本身。而在Java中,\这个字符本身也是特殊字符,需要转义,\\表示的才是真正的\这个字符,所以,想在正则表达中真正表示$这个字符,在正则中就要写成\\$,这就有了代码中的写法。

而匹配后,处理字符串“###xx$$$”,要删除的字符串长度是3("$$$"的长度),而不是"\$\$\$"(其实调试的话会发现,Match匹配到的是“\$\$\$”)。

以后再遇到需要显示Html中的标签时,这是个不错的选择,特别是自定义的TextView本身已用到Span去处理东西时,比如实现“...    展开”这种处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值