SpannableString與SpannableStringBuilder

場景

在實際開發(fā)中,經(jīng)常會出現(xiàn)一些富文本的樣式(如:局部文本可點擊,超鏈接,一段文本字體大小不一,局部文本加粗等),這樣常規(guī)的設(shè)置文本就無法實現(xiàn)目的了。

針對這種場景一些常規(guī)的實現(xiàn):

  1. 利用多個View的排布來實現(xiàn);
  2. 利用Html實現(xiàn)富文本格式,在通過Html.getText(html)來解析;
  3. 利用SpannableString;

方案分析:

  1. 使用方案一會在布局中新增出多個View,導(dǎo)致布局文件臃腫,并且View的inflate是一件比較耗時的操作。當然在一些特殊的場景,為了簡單快速的實現(xiàn)目的,這種方案也有應(yīng)用。另外就是采用此方案有時并不能完美的實現(xiàn)我們想要的功能;
  2. 使用方案二,優(yōu)點是可以很好的實現(xiàn)我們想要的文本樣式,但缺點也很明顯,Html.getText()方法解析html標簽是一個很消耗性能的操作,一般情非得已不推薦使用此方案;
  3. 方案三就是本篇要介紹的對象,SpannableString是Android提供專門用于處理富文本的類,根據(jù)設(shè)置不同的span來實現(xiàn)不同的文本樣式。使用一個TextView通過span的處理,即可實現(xiàn)目的。

API

SpannaableString與SpannableStringBuilder的區(qū)別就在于builder可以使用insert、append等系列方法。

  1. public void setSpan():設(shè)置span,一切富文本的實現(xiàn)都是通過此方法的來設(shè)置不同效果的span來實現(xiàn)的。

    /**
     *
     * @param what  具體的實現(xiàn)效果的Span類對象
     * @param start span生效的起始下標
     * @param end span生效的終止下標+1
     * @param flags span效果針對臨界位置的insert是否擴散生效
     */
    public void setSpan(Object what, int start, int end, int flags) {
    }
    
  2. public void removeSpan(Object what):移除指定的span;

  3. insert/delete/append等方法是SpannableStringBuilder所有的方法系列,其中setSpan()中的flags參數(shù)就是配合這些方法的使用而發(fā)揮效果的。

    flags的選項在Spanned接口中,分別為:

    • SPAN_INCLUSIVE_EXCLUSIVE:包含start,不包含end
    • SPAN_INCLUSIVE_INCLUSIVE:start,end都包含
    • SPAN_EXCLUSIVE_EXCLUSIVE:start,end都不包含
    • SPAN_EXCLUSIVE_INCLUSIVE:start不包含,end包含

    具體生效備注如下:

    Spanned中flags的標記,是在SpannableStringBuilder中使用的,在SpannableString中沒有作用。當使用了SpannableStringBuilder.insert(int,CharSeque)系列方法后,對于insert后的字符串是否進行擴展特性的標記,并且此標記作用的場景也僅僅是insert的位置恰好處于start或者end兩個端點的臨界位置,即用flags標記這個臨界點跟隨哪個,是否使用span的特性。

各種Span

  1. ForegroundColorSpan

    用于設(shè)置前景色,即設(shè)置字體的顏色

    void forefroundColorSpan() {
        TextView tv1 = new TextView(activity);
        /**
         * Spanned中flags的標記,是在SpannableStringBuilder中使用的,在SpannableString中沒有作用。并且其使用場景是當
         使用了SpannableStringBuilder.insert(int,CharSeque)方法后,,對于insert后的字符串是否進行擴展特性的標記,,
         并且此標記作用的場景也僅僅是insert的位置恰好處于start 或者 end兩個端點的臨界位置,,,即用flags標記這個臨界點跟隨哪個。
         * */
        SpannableStringBuilder ss = new SpannableStringBuilder("手續(xù)費84.00元");
        ForegroundColorSpan span1 = new ForegroundColorSpan(Color.RED);
        ss.setSpan(span1, 3, 8, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        tv1.setText(ss);
        rootSpannable.addView(tv1);
        //插入的9擴展了特性,而插入的5未擴展特性
        ss.insert(3, "9").insert(9, "5");
        TextView tv2 = new TextView(activity);
        tv2.setText(ss);
        rootSpannable.addView(tv2);
        //移除指定span
        ss.removeSpan(span1);
        TextView tv5 = new TextView(activity);
        tv5.setText(ss);
        rootSpannable.addView(tv5);
        //恢復(fù)ss
        ss = new SpannableStringBuilder("手續(xù)費84.00元");
        ss.setSpan(span1, 3, 8, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        //驗證同樣的情況,只要執(zhí)行insert系列方法,,即使flags改變效果也不變
        TextView tv3 = new TextView(activity);
        tv3.setText(ss);
        rootSpannable.addView(tv3);
        //插入的9未擴展特性,而插入的5擴展了特性
        ss.insert(3, "9").insert(9, "5");
        TextView tv4 = new TextView(activity);
        tv4.setText(ss);
        rootSpannable.addView(tv4);
    }
    

    效果圖如下:

spannableString-forespan.png
  1. ClickableSpan

    設(shè)置局部文本可點擊

    void clickableSpan() {
        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                ds.setColor(Color.BLUE);//控制可點擊文案的字體顏色
                ds.setUnderlineText(false);//可點擊文案是否具有下劃線
            }
    
            @Override
            public void onClick(View widget) {
                MyToast.showShort(getContext(), "clicked");
            }
        };
        SpannableString ss = getSS();
        ss.setSpan(clickableSpan, 0, 3, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        TextView tv = getTv();
        tv.setText(ss);
        tv.setMovementMethod(LinkMovementMethod.getInstance());
        //一般由于頁面的數(shù)據(jù)刷險等原因,為避免重復(fù)設(shè)置MovementMethod,在setMovementMethod之前做如下下判斷
        /*MovementMethod m = tv.getMovementMethod();
        if (m == null || !(m instanceof LinkMovementMethod)) {
            if (tv.getLinksClickable()) {
                tv.setMovementMethod(LinkMovementMethod.getInstance());
            }
        }*/
        tv.setHighlightColor(Color.YELLOW);//更改可點擊區(qū)域文本,觸摸或者點擊后的背景高亮的顯示顏色
    }
    

    效果如下:

clickspan.gif
  1. BackgroundColorSpan

    背景色,設(shè)置局部的TextView具有背景

  2. MaskFilterSpan

    修飾效果,如模糊(BlurMaskFilter)浮雕(EmbossMaskFilter)

  3. RasterizerSpan

    光柵效果

  4. StrikethroughSpan

    刪除線(中間線)

  5. SuggestionSpan

    相當于占位符

  6. UnderlineSpan

    下劃線

  7. AbsoluteSizeSpan

    絕對大?。ㄎ谋咀煮w)

    // 設(shè)置字體絕對大?。ń^對值,單位:像素)
    msp.setSpan(new AbsoluteSizeSpan(20), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    // 第二個參數(shù)boolean dip,如果為true,表示前面的字體大小單位為dip,否則為像素,同上。
    msp.setSpan(new AbsoluteSizeSpan(20, true), 6, 8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    
  8. DynamicDrawableSpan

    設(shè)置圖片,基于文本基線或者底部對齊

  9. ImageSpan

    圖片

  10. RelativeSizeSpan

    相對大?。ㄎ谋咀煮w)

    設(shè)置字體相對大小(相對值,單位:像素)參數(shù)表示為默認字體大小的多少倍

    msp.setSpan(new RelativeSizeSpan(0.5f), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 0.5f表示默認字體大小的一半
    
  11. ReplacementSpan

    父類,一般不用

  12. MetricAffectingSpan

    父類,一般不用

  13. ScaleSpan

    基于X軸縮放

  14. StyleSpan

    new StyleSpan(Typeface.BOLD)
    

    字體樣式:粗體、斜體等

  15. SubscriptSpan

    下標(數(shù)學公式會用到)

  16. SuperscriptSpan

    上標(數(shù)學公式會用到)

  17. TextAppearanceSpan

    文本外貌(包括字體、大小、樣式、顏色)

  18. TypefaceSpan

    文本字體

  19. URLSpan

    文本超鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容