(写在题前,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去处理东西时,比如实现“... 展开”这种处理。