TextView使用Spannable设置复合文本

端午过后又一天,还是没事干,再写一篇博客,过后就要期末考试了,可能最近就不出博文了,等暑假再重出江湖吧。

今天来弄弄这个TextView的效果,应用场景还是很广泛的,一个TextView文本拥有各种各样的样式,以前给文本插入图片都是那种drawableLeft啥的right的,图片大小还不好控制,排版又不好看,一次只能插入一张图片,根本满足不了需求,比如一些数学公式的上标和下标,纯文本来打肯定是打不起来的,之前很早就看到Spannable了,大概是14年暑假的时候玩emoj表情看到了这个功能,发现文本还能插入图片,感觉挺好玩的,时隔多年了没有去管他,现在想想,有点后悔了,今天就来了解了解他吧。


Spannable是个接口有两个实现类,如下:

Interface Spannable
    -----SpannableString
    -----SpannableStringBuilder 

我们先来今天主要是看看SpannableString.class这个类,SpannableStringBuilder 只不过是用来整合SpannableString的各个效果,所以,关键还在于SpannableString的效果。

来看看API的解释:

This is the class for text whose content is immutable but to which markup objects can be attached and detached. For mutable text, see SpannableStringBuilder.

这类文本的内容是不变的,但标记对象可以被附加和分离。可变的文本,请参阅SpannableStringBuilder。

这个类最主要的一个设置效果的方法:

public void setSpan(Object what,int start, int end, int flags)

来看看解释:

Attach the specified markup object to the range start…end of the text, or move the object to that range if it was already attached elsewhere. See Spanned for an explanation of what the flags mean. The object can be one that has meaning only within your application, or it can be one that the text system will use to affect text display or behavior. Some noteworthy ones are the subclasses of CharacterStyle and ParagraphStyle, and TextWatcher and SpanWatcher.

把指定的标记对象文本的范围开始…结束,或移动对象,如果已经连接到其他地方。看到了一个解释的旗子是什么意思。对象可以有意义只在您的应用程序,也可以是一个文本系统将使用影响文本显示或行为。一些值得注意的是子类CharacterStyle和ParagraphStyle TextWatcher和SpanWatcher

这里呢,我们会通过这个方法来延伸今天的主题,我们先来看看方法最后一个参数flag的意思.
这里写图片描述

这个是API上面列出来的,我们今天主要讲常用的四个,其他的大家自己去探索吧。
接下来是解释这些用法,用有道翻译了下,别介意^-^.

static final int SPAN_EXCLUSIVE_EXCLUSIVE

英文解释:Spans of type SPAN_EXCLUSIVE_EXCLUSIVE do not expand to include text inserted at either their starting or ending point. They can never have a length of 0 and are automatically removed from the buffer if all the text they cover is removed.
中文解释:如果start-end为0的话,插入就没有效果,也就是上面说的:“他们永远不会有一个长度为0会自动从缓冲区中删除”,如果要覆盖的话就包含他们的起点为0,end为文本长度,为:“如果删除它们覆盖所有的文本”。

static final int SPAN_EXCLUSIVE_INCLUSIVE

英文解释:Non-0-length spans of type SPAN_EXCLUSIVE_INCLUSIVE expand to include text inserted at their ending point but not at their starting point. When 0-length, they behave like points.
中文解释:Non-0-length跨越SPAN_EXCLUSIVE_INCLUSIVE类型的扩展到包含文本插入到终点而不是他们的起点。当0-length时,他们表现得像点。

static final int SPAN_INCLUSIVE_EXCLUSIVE

英文解释:Non-0-length spans of type SPAN_INCLUSIVE_EXCLUSIVE expand to include text inserted at their starting point but not at their ending point. When 0-length, they behave like marks.
中文解释:Non-0-length跨越SPAN_INCLUSIVE_EXCLUSIVE类型的扩展到包含文本插入到自己的起点而不是终点。当0-length,他们表现得像标志。

static final int SPAN_INCLUSIVE_INCLUSIVE

英文解释:Spans of type SPAN_INCLUSIVE_INCLUSIVE expand to include text inserted at either their starting or ending point.
中文解释:跨越SPAN_INCLUSIVE_INCLUSIVE类型的扩展到包含文本插入他们的开始或结束。

关于上面的flag更多的解释请看这个哥们的

好了,接下来就是看参数Object what了,这个才是重头戏呢,效果显示都在这。
我们来看看这里面都放的是啥,上面的解释说了,里面可以放CharacterStyle和ParagraphStyle TextWatcher和SpanWatcher这四个类,我们主要来看看CharacterStyle.class这个类,我们来看看他的实现子类。
这里写图片描述

  • 子类实现列表
BackgroundColorSpan
ForegroundColorSpan
ClickableSpan
    ---URLSpan 
MaskFilterSpan
MetricAffectingSpan
    ---AbsoluteSizeSpan
    ---LocaleSpan
    ---ReplacementSpan
       ---DynamicDrawableSpan 
          ---ImageSpan
    ---ScaleXSpan 
    ---StyleSpan
    ---SubscriptSpan
    ---SuperscriptSpan
    ---TextAppearanceSpan
    ---TypefaceSpan 
RasterizerSpan
StrikethroughSpan
SuggestionRangeSpan
SuggestionSpan
UnderlineSpan 

接下来我们一一实验看看效果。
SpannableString初始化代码

SpannableString str = new SpannableString("我是来自歙县的小马达");
  • BackgroundColorSpan.class
 BackgroundColorSpan back = new BackgroundColorSpan(0xffff0000);
 str.setSpan(back, 3,7, SpannableString.SPAN_INCLUSIVE_INCLUSIVE);

这里写图片描述

  • ForegroundColorSpan.class

    ForegroundColorSpan fore=new ForegroundColorSpan(0xff00ff00);
    str.setSpan(fore, 3, 7, SpannableString.SPAN_INCLUSIVE_INCLUSIVE);

    这里写图片描述
  • ClickableSpan.class

    //设置文本点击的时候必须设置这句话,不然不起作用
    text.setMovementMethod(LinkMovementMethod.getInstance());
    
    ClickableSpan click = new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                //设置超链接样式
                ds.setColor(0xffff0000);
                ds.setUnderlineText(false);
                System.out.println("updateDrawState");
                ds.clearShadowLayer();
            }
            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this, "nihai", 0).show();
            }
        };
        str.setSpan(click, 3, 7, SpannableString.SPAN_INCLUSIVE_INCLUSIVE);

    还有一种方式就是设置TextView的属性来设置可点击文本的样式。
    android:textColorLink=”@drawable/icon”
    要是想设置点击下去的样式,就设置drawable为selector。
    这里写图片描述

  • URLSpan.class

    text.setMovementMethod(LinkMovementMethod.getInstance());
    URLSpan url=new URLSpan("http://www.lontano.top");
    str.setSpan(url, 3, 7, SpannableString.SPAN_INCLUSIVE_INCLUSIVE);

    这个效果图就不贴了,和上面一样,只不过点击的时候,会触发本地的浏览器,打开一个网页,这个拿来做公司介绍跳转链接挺好用。

  • UnderlineSpan.class

    UnderlineSpan under=new UnderlineSpan();
    str.setSpan(under, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    这里写图片描述

  • StrikethroughSpan.class

    StrikethroughSpan strick=new StrikethroughSpan();
    str.setSpan(strick, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    这里写图片描述

  • MaskFilterSpan.class

    BlurMaskFilter filter = new BlurMaskFilter(50.0f, Blur.SOLID);
    MaskFilterSpan mask = new MaskFilterSpan(filter);
    str.setSpan(mask, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    MaskFilterSpan这个类里面接收一个MaskFilter类,这个MaskFilter类有三个子类,分别是:
    BlurMaskFilter.class://模糊效果
    EmbossMaskFilter.class://浮雕效果
    TableMaskFilter.class://–有道里面没有这个翻译。。。。
    这里写图片描述

  • AbsoluteSizeSpan.class

    AbsoluteSizeSpan absoluteSizeSpan=new AbsoluteSizeSpan(90);
    str.setSpan(absoluteSizeSpan, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    该功能是实现字体的大小
    这里写图片描述

  • LocaleSpan.class

    LocaleSpan localeSpan=new LocaleSpan(Locale.CANADA);
    str.setSpan(localeSpan, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    用了下,不是有太多的明显效果,这个类是用来设置地区语言的,看来的下api我感觉效果应该是为了区别各个国家对字符的处理不同吧,这是api上面翻译:
    —英文解释:
    For example, if you’re formatting integers some locales will use non-ASCII decimal digits. As another example, if you’re formatting floating-point numbers some locales will use ‘,’ as the decimal point and ‘.’ for digit grouping. That’s correct for human-readable output, but likely to cause problems if presented to another computer (Double.parseDouble(java.lang.String) can’t parse such a number, for example). You should also be wary of the String.toLowerCase() and String.toUpperCase() overloads that don’t take a Locale: in Turkey, for example, the characters ‘i’ and ‘I’ won’t be converted to ‘I’ and ‘i’. This is the correct behavior for Turkish text (such as user input), but inappropriate for, say, HTTP headers.
    — 中文解释:
    例如,如果你格式化整数一些地区将使用非ascii小数位数。另外一个例子,如果你格式化浮点数一些地区将使用”、“小数点和”。对数字分组。这对人类可读的输出是正确的,但可能导致问题如果向另一台计算机(Double.parseDouble(以)不能解析这样的一个数字,例如)。你还应该警惕的String.toLowerCase()和String.toUpperCase()过载,不要把一个语言环境:在土耳其,例如,人物“我”和“我”不会转换为“我”和“我”。这是正确的行为对于土耳其文本(比如用户输入),但不适合,说,HTTP头。

  • ImageSpan.class

    Drawable d = getResources().getDrawable(R.drawable.a);
             d.setBounds(0, 0, 100, 100);
    ImageSpan img = new ImageSpan(d);
    str.setSpan(img, 3, 7,SpannableString.SPAN_EXCLUSIVE_INCLUSIVE);

    有些人使用ImageSpan的时候,喜欢给ImageSpan的构造函数直接传入图片资源ID,虽然是能插进去啊,但是你不能设置图片的大小,排版就一点都不好玩了
    这里写图片描述

  • ScaleXSpan.class

    ScaleXSpan scaleXSpan=new ScaleXSpan(3.8f);
    str.setSpan(scaleXSpan, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);    

    这个类的使用是对X轴方向的缩放,我之前还以为会有个Y轴的缩放,但是好像没看到,然后敲了一下这个Y轴的类,还真没有,最后我想了一下,字符串一般都是横向写入,要是再来个Y轴方向的伸缩的话,岂不破坏了TetxView的height高度,所以google的工程师没写,我是这么猜的。。。。
    这里写图片描述

  • StyleSpan.class

    StyleSpan style=new StyleSpan(Typeface.BOLD_ITALIC);
    str.setSpan(style, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    Typeface里面有很多效果,你们自己尝试尝试
    这里写图片描述

  • SubscriptSpan.class

    SubscriptSpan subscriptSpan=new SubscriptSpan();
    str.setSpan(subscriptSpan, 3, 4, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    这个是设置下标,使用一些数学符号,SuperscriptSpan这个类是上标,我就不演示了,效果基本能上差不多,对了,再多嘴一句,要想实现数学公式那种效果,最好是使用第三方框架MathView
    这里写图片描述

  • TextAppearanceSpan.class

    TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(this,R.style.txtStyle);
    str.setSpan(textAppearanceSpan, 3, 7,SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    这个类主要是用来引用style文件里面的格式集合,
    这里写图片描述

  • TypefaceSpan.class

    TypefaceSpan typefaceSpan=new TypefaceSpan("serif");
    str.setSpan(typefaceSpan, 3, 7, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);

    这个类好像是用来设置字体的,看了下API给的说明,只有三种字体
    family - The font family for this typeface. Examples include “monospace”, “serif”, and “sans-serif”.
    但是我用了一下,好像没啥效果,不知道啥意思,管他呢。

最后结束了,要用上整合整个格式的类SpannableStringBuilder.class了

  • SpannableStringBuilder.class

    SpannableStringBuilder builder = new SpannableStringBuilder();
    
        SpannableString str1 = new SpannableString("我是");
        StyleSpan style = new StyleSpan(Typeface.BOLD_ITALIC);
        str1.setSpan(style, 0, str1.length(),
                SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    
        SpannableString str2 = new SpannableString("来自");
        SubscriptSpan subscriptSpan = new SubscriptSpan();
        str2.setSpan(subscriptSpan, 0, str2.length(),
                SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    
        SpannableString str3 = new SpannableString("歙县的");
        ClickableSpan click = new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                // 设置超链接样式
                ds.setColor(0xffff0000);
                ds.setUnderlineText(false);
                ds.clearShadowLayer();
            }
            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this, "666", 0).show();
            }
        };
        str3.setSpan(click, 0, str3.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    
    
        SpannableString str4 = new SpannableString("小马达");
        BackgroundColorSpan back = new BackgroundColorSpan(0xff00ff00);
        str4.setSpan(back, 0,str4.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    
        SpannableString str5 = new SpannableString("越跑越6");
        UnderlineSpan under=new UnderlineSpan();
        str5.setSpan(under, 0,str5.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    
        builder.append(str1);
        builder.append(str2);
        builder.append(str3);
        builder.append(str4);
        builder.append(str5);
        text.setMovementMethod(LinkMovementMethod.getInstance());
        text.setText(builder);

这里写图片描述

真的是实践出真理哦,我前面所述的那个flag刚开始写的时候我没理解清楚,最后实践的时候,我发现了问题,原来设置SPAN_EXCLUSIVE_EXCLUSIVE,他的格式不会扩散给后面的文本,然而设置SPAN_INCLUSIVE_INCLUSIVE时,后面的格式都被前面的格式又累加了一层上去,影响了后面的格式排版,错误示范给你们看看,我把上面的SPAN_EXCLUSIVE_EXCLUSIVE全部换成SPAN_INCLUSIVE_INCLUSIVE给你们看看效果图。
这里写图片描述

全部替换后,后面的格全被影响了,一直加粗到后面,前面的下标也影响到后面的所有,然后接下来的斜体继续影响后面,点击事件点击“越跑越6”也有了反应,所以,在做整合不同格式的时候最好使用SPAN_EXCLUSIVE_EXCLUSIVE这个flag,我去,真是好险,还好弄懂了,学习还是不能唬自己啊。


好了好了,常见的说的差不多了,有些可能没写,要是有好的大家记得留言给我,我再来改改。
这次写的博文虽然没什么技巧,只是一些普通的使用流程而已,但是,却收获了不少,以前喜欢网上看别人的使用方法,没有自己深入的去探索,现在呢,这次结合API和自己的实际操作,英语不好然后用有道来翻译了一下,虽然时间用了好久,但是慢慢的,很享受这个过程,未来之际,希望再接再厉,么么哒

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值