Android 多样化显示TextView以及扩展Html自定义标签

一、前言

TextView作为一个我们经常使用到的控件,特别是在写界面的时候肯定都遇到过以下的几种情况(直接用图片来表达是最清晰的)
第一种:
我是文本1

第二种:
我是文本2

二、多样化显示TextView

那么让我们首先来说说第一种,其实这种不同大小不同样式的TextView我们直接选择使用SpannableString就可以实现了,这里就简单的带过,比如我们要进行文本颜色的设置:

SpannableString spanStr = new SpannableString("blue");
spanStr.setSpan(new ForegroundColorSpan(Color.BLUE), 2,spanStr.length(),Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spanStr);

下面就是他可以进行设置的一些类
1. BackgroundColorSpan 背景色
2. ClickableSpan 文本可点击,有点击事件
3. ForegroundColorSpan 文本颜色(前景色)
4. MaskFilterSpan修饰效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter)
5. MetricAffectingSpan 父类,一般不用
6. RasterizerSpan 光栅效果
7. StrikethroughSpan 删除线(中划线)
8. SuggestionSpan 相当于占位符
9. UnderlineSpan 下划线
10. AbsoluteSizeSpan 绝对大小(文本字体)
11. DynamicDrawableSpan 设置图片,基于文本基线或底部对齐。
12. ImageSpan 图片
13. RelativeSizeSpan 相对大小(文本字体)
14. ReplacementSpan 父类,一般不用
15. ScaleXSpan 基于x轴缩放
16. StyleSpan 字体样式:粗体、斜体等
17. SubscriptSpan 下标(数学公式会用到)
18. SuperscriptSpan 上标(数学公式会用到)
19. TextAppearanceSpan 文本外貌(包括字体、大小、样式和颜色)
20. TypefaceSpan 文本字体
21. URLSpan 文本超链接

2.1、总结

这一种TextView的使用场景一般是需要动态的改变文本的颜色,比如搜索功能将关键字高亮显示等等,打开高德地图进行搜索地址的时候就会有这种效果,如果只是静态的显示这些文本,我更推荐使用线性或者相对布局进行一个个文本的拼接


三、扩展Html自定义标签

本文主要说的是第二种方式,相信第一眼看过去一定会觉得博主是不是脑子有坑,这不就是三个不同颜色的TextView吗?请容我慢慢道来。

3.1、使用场景

其实我们写代码最终的目的是为了解决问题,实现我们想要的结果,所以我们在不同的使用场景下就需要有不同的实现方式。
现在有这样一种场景,有一个活动页面,上面文本的颜色和大小等等属性是会经常性要进行变更的(先排除使用H5页面),那我们肯定是需要通过获取后端的协议得到相对应的文本进行显示,按我们一般的写法会让后端给予我们相对应数量的文本进行显示,颜色和大小只能由我们写死了,有些朋友会说可以传递颜色和大小过来,这样就得接受好几个对象然后再解析获得颜色大小进行设置,如果有更多的文本怎么办,相信这样下去后端和你都会觉得越来越麻烦而去把产品给砍死的,为了防止这种情况发生,有没有办法只用一个string就可以搞定这样多样式的文本呢?

3.2、思路

看了一下第一种方式,直接使用SpannableString明显是不可行的,因为我们必须知道他的具体长度,那么只能够换一种方式实现了,相信有写过Html的大神们都知道其实Android有一个类叫Html,里面是支持我们Html格式的字符串转换为文本的,那么这时候思路就很清晰了,我们只需要接收Html格式的String,然后使用Html.fromHtml方法就可以将他转换为我们想要的多样式TextView!,马上动手试试。

3.3、代码

我们定义一个String假装他是服务器传递过来的数据进行显示看看结果是怎么样的,首先是来一个html格式的字符串

String htmlStr = "<font color='#0000FF' size='50px'>我是蓝色的文本</font><br><font color='#ff0000' size='40px'>我是红色的文本</font><br><font color='#000000' size='29px'>我是黑色的文本</font>";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextView = new TextView(this);
    mTextView.setText(Html.fromHtml(htmlStr));
}

哈哈,这么简单的代码对于我们来说不就是分分钟就搞定吗,马上就来验证一下自己的成果
test
我擦,颜色出来了,但是字体大小怎么完全没改变,你是不是在逗我,凭我多年写Html的Hello World语句来说我的Html文本肯定没有错!,立马找一下原因是为什么,让我们来看看Html.fromHtml都做了什么事情。

test2
从源码里可以看到他会去定义一个SAX解析类Parser,然后传递到133行HtmlToSpannedConverter的构造方法里,并且调用这个类的conver()方法,那我们先看看这个类里面都做了什么
test
简单过一下构造方法,知道他都有什么

test4
如果有写过SAX解析的朋友现在肯定不会陌生,首先是去设置一个文档内容的处理器,进行XML的解析,里面就是一些头节点尾节点元素开头结束等等的XML相关处理,然后调用parser进行解析,然后会走回调方法,就列出我们比较关心的头尾方法

test
然后进行节点的处理

1
2
这时候眼睛比较凌厉的朋友已经发现我们最想要知道的代码了!他是如何去处理font这个标签的,让我们来看看这个方法startFont(mSpannableStringBuilder, attributes);

1
看到这里的时候我的心里是奔溃的。。尼玛这都什么跟什么,怎么就支持color这个标签,不支持size,还有这face是什么鬼,能支持face难道不能支持我传说中的大size么!!简直是在逗我!!
在这里我们可以看出来他的实现方式其实很简单,首先是使用XML去解析每一个节点,然后使用SpannableStringBuilder去进行拼接。

到了这个时候我们只能够自己去定义实现font以及获取里面的属性了,要怎么做呢,我们可以看到其实他在这一大堆if else的判断里面已经把font这个标签给处理掉了,不会给我们继续处理(不要跟我说修改源码),这时候其实我们看一下if else的最后,他是会进行回调到一个叫TagHandler里面的方法的,那么我们只需要去实现这个接口就可以了,从上面可以看出来,他是一个抽象类,用于给我们扩展的

上面说到了font标签已经被处理掉了,不会再回调给我们,所以我们就需要自己去定义一个标签,当然了,后端给予我们得一样可以是font的,我们只是自己去进行替换,类似这样子

htmlStr = htmlStr.replaceAll("font", "bluefont");
mTextView.setText(Html.fromHtml(htmlStr, null, new HtmlTagHandler()));

首先是替换成一个源码里不会进行处理的标签

/**
 * Created by blue.
 */
public class HtmlTagHandler implements Html.TagHandler {
    private static final String TAG_BLUE_FONT = "bluefont";

    private int startIndex = 0;
    private int stopIndex = 0;
    final HashMap<String, String> attributes = new HashMap<String, String>();

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        processAttributes(xmlReader);

        if(tag.equalsIgnoreCase(TAG_BLUE_FONT)){
            if(opening){
                startFont(tag, output, xmlReader);
            }else{
                endFont(tag, output, xmlReader);
            }
        }
    }

    public void startFont(String tag, Editable output, XMLReader xmlReader) {
        startIndex = output.length();
    }

    public void endFont(String tag, Editable output, XMLReader xmlReader){
        stopIndex = output.length();

        String color = attributes.get("color");
        String size = attributes.get("size");
        size = size.split("px")[0];

if(!TextUtils.isEmpty(color) && !TextUtils.isEmpty(size)){
            output.setSpan(new ForegroundColorSpan(Color.parseColor(color)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
        if(!TextUtils.isEmpty(size)){
            output.setSpan(new AbsoluteSizeSpan(Integer.parseInt(size)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    private void processAttributes(final XMLReader xmlReader) {
        try {
            Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");
            elementField.setAccessible(true);
            Object element = elementField.get(xmlReader);
            Field attsField = element.getClass().getDeclaredField("theAtts");
            attsField.setAccessible(true);
            Object atts = attsField.get(element);
            Field dataField = atts.getClass().getDeclaredField("data");
            dataField.setAccessible(true);
            String[] data = (String[])dataField.get(atts);
            Field lengthField = atts.getClass().getDeclaredField("length");
            lengthField.setAccessible(true);
            int len = (Integer)lengthField.get(atts);

            for(int i = 0; i < len; i++){
                attributes.put(data[i * 5 + 1], data[i * 5 + 4]);
            }
        }
        catch (Exception e) {

        }
    }

}

使用startIndex和stopIndex来进行判断每一个标签头和尾的位置,对里面的文本进行相对应的样式处理。然后让我们来运行一下代码试试

test
恩!?为什么第一行的样式不起效果呢??其实这个只是SAX解析的一些小bug而已,具体原理的话稍后我再贴上来,解决的方案也很简单:
1)在这一段Html格式的字符串前面再加上随意一个标签,例如<p>标签等等
2)发送html格式的字符串过来的时候将<html><body>也就是一整个网页需要的信息传递过来,也可以解决这个问题

那么修复了这个小bug后让我们来看看我们的最终成功,一个字段显示多种不同样式的文本
2

四、总结

上面的这些都是抛砖引玉,带来一些思路,我们可以自己进行扩展所有的Html标签,Android自带能支持的标签实在是太少了,而且连</br>都不能带斜杠得写成<br>不然不能正常得换行。

五、源码

Android 多样化显示TextView以及扩展Html自定义标签

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值