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

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

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


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

    SpannableStringBuilder spanBuilder=new SpannableStringBuilder(title);
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"","");
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"","");
    spanBuilder=getHtmlSpan(spanBuilder,textPaint,"","");
    //期待更多支持的标签。。。
    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("".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("".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("".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方法不会影响到本身已设置的富文本效果,索引不会自动移位。这保证了对15N度这种文本处理后,删除“”和“”字符串字样不会影响“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去处理东西时,比如实现“...    展开”这种处理。

你可能感兴趣的:(Android,高级,html,java,servlet,android)