TextView 那些鮮爲人知的方法

綜述


一起先看看官方文檔怎麼說的:

Displays text to the user and optionally allows them to edit it. A TextView is a complete text editor, however the basic class is configured to not allow editing; see EditText for a subclass that configures the text view for editing.

To allow users to copy some or all of the TextView’s value and paste it somewhere else, set the XML attribute android:textIsSelectable to “true” or call setTextIsSelectable(true). The textIsSelectable flag allows users to make selection gestures in the TextView, which in turn triggers the system’s built-in copy/paste controls.

這個是官方文檔給的概述,這是一個展示文本的一個基類,是EditText的父類,不允許用戶編輯,如果想要配置這個類的話,去看看EditText。如果想讓用戶長按複製的話,可以在xml文件中配置屬性android:textIsSelectable=”true”,也可以在代碼中設置,setTextIsSelectable(true)即可,相信大家在也熟悉不過了,這次我們瞭解下TextView鮮爲人知的用法。究竟什麼用法呢,先看看需求吧,有需求才有實現麼。

需求



這裏寫圖片描述
這個是我從Uber乘車軟件上截取下來的,因爲那時候項目有這個需求。基本就是這樣的,上面那段“當有…..詳細信息”那塊你怎麼實現!難道你還用兩個TextView拼接在一起嚒?顯然不適合,因爲,Android設備尺寸,分辨率什麼的太多了。很難做到適應每個屏幕。

實現



接下來一起實現下吧,用多個TextView的方式肯定是不行的,得排除掉。那麼如何用一個TextView顯示不同顏色的文本呢?這裏我們有兩種方式可以實現:

一、使用Html格式化文字。

  String content = "當好友使用您的優惠碼搭乘時,你們都\n將獲得¥30!<font color='red'>詳細信息</font>";
        uber1.setText(Html.fromHtml(content));

這樣我們可以通過寫Html的方式來顯示文本,可以控制它的大小,顏色,字體。但是,這種解析也並不一定是萬能的,我們跟進Html的源碼看看:

   private void handleStartTag(String tag, Attributes attributes) {
        if (tag.equalsIgnoreCase("br")) {
            // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>
            // so we can safely emite the linebreaks when we handle the close tag.
        } else if (tag.equalsIgnoreCase("p")) {
            handleP(mSpannableStringBuilder);
        } else if (tag.equalsIgnoreCase("div")) {
            handleP(mSpannableStringBuilder);
        } else if (tag.equalsIgnoreCase("strong")) {
            start(mSpannableStringBuilder, new Bold());
        } else if (tag.equalsIgnoreCase("b")) {
            start(mSpannableStringBuilder, new Bold());
        } else if (tag.equalsIgnoreCase("em")) {
            start(mSpannableStringBuilder, new Italic());
        } else if (tag.equalsIgnoreCase("cite")) {
            start(mSpannableStringBuilder, new Italic());
        } else if (tag.equalsIgnoreCase("dfn")) {
            start(mSpannableStringBuilder, new Italic());
        } else if (tag.equalsIgnoreCase("i")) {
            start(mSpannableStringBuilder, new Italic());
        } else if (tag.equalsIgnoreCase("big")) {
            start(mSpannableStringBuilder, new Big());
        } else if (tag.equalsIgnoreCase("small")) {
            start(mSpannableStringBuilder, new Small());
        } else if (tag.equalsIgnoreCase("font")) {
            startFont(mSpannableStringBuilder, attributes);
        } else if (tag.equalsIgnoreCase("blockquote")) {
            handleP(mSpannableStringBuilder);
            start(mSpannableStringBuilder, new Blockquote());
        } else if (tag.equalsIgnoreCase("tt")) {
            start(mSpannableStringBuilder, new Monospace());
        } else if (tag.equalsIgnoreCase("a")) {
            startA(mSpannableStringBuilder, attributes);
        } else if (tag.equalsIgnoreCase("u")) {
            start(mSpannableStringBuilder, new Underline());
        } else if (tag.equalsIgnoreCase("sup")) {
            start(mSpannableStringBuilder, new Super());
        } else if (tag.equalsIgnoreCase("sub")) {
            start(mSpannableStringBuilder, new Sub());
        } else if (tag.length() == 2 &&
                   Character.toLowerCase(tag.charAt(0)) == 'h' &&
                   tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
            handleP(mSpannableStringBuilder);
            start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1'));
        } else if (tag.equalsIgnoreCase("img")) {
            startImg(mSpannableStringBuilder, attributes, mImageGetter);
        } else if (mTagHandler != null) {
            mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader);
        }
    }

這個是解析開始標籤的方法,我們可以看到,他只支持這些標籤,但是Google還做了一些拓展,如果你用到了其他標籤或者是自定義標籤,你可以通過實現

 /**
     * Is notified when HTML tags are encountered that the parser does
     * not know how to interpret.
     */
    public static interface TagHandler {
        /**
         * This method will be called whenn the HTML parser encounters
         * a tag that it does not know how to interpret.
         */
        public void handleTag(boolean opening, String tag,
                                 Editable output, XMLReader xmlReader);
    }

這個接口,來實現自己的業務需求。有一點需要注意的是,如果你想在TextView上用<image>標籤時,還需要實現一個接口:

/**
     * Retrieves images for HTML &lt;img&gt; tags.
     */
    public static interface ImageGetter {
        /**
         * This method is called when the HTML parser encounters an
         * &lt;img&gt; tag.  The <code>source</code> argument is the
         * string from the "src" attribute; the return value should be
         * a Drawable representation of the image or <code>null</code>
         * for a generic replacement image.  Make sure you call
         * setBounds() on your Drawable if it doesn't already have
         * its bounds set.
         */
        public Drawable getDrawable(String source);
    }

其中的英文解釋的已經很清楚了,這裏也不做贅述了,但是它強調的是一定要給圖片設置邊界,然後再forHtml()方法中將自己實現的接口放進去就行了。

二、 通過SpannableString來設置文本內容
通過這個類可以給他設置各種類型的span,常見的span的類型有一下這些,都通過一些實例來說明用法:

//設置字體(default,default-bold,monospace,serif,sans-serif)  msp.setSpan(new TypefaceSpan("monospace"), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//設置字體大小(絕對值,單位:像素) 
 msp.setSpan(new AbsoluteSizeSpan(20), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//設置字體大小(相對值,單位:像素) 參數表示爲默認字體大小的多少倍  msp.setSpan(new RelativeSizeSpan(0.5f), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //0.5f表示默認字體大小的一半

//設置字體前景色 
msp.setSpan(new ForegroundColorSpan(Color.MAGENTA), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //設置前景色爲洋紅色

//設置字體背景色 
 msp.setSpan(new BackgroundColorSpan(Color.CYAN), 15, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //設置背景色爲青色

//設置字體樣式正常,粗體,斜體,粗斜體 
msp.setSpan(new StyleSpan(android.graphics.Typeface.NORMAL), 18, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //正常

//設置下劃線 
msp.setSpan(new UnderlineSpan(), 27, 30, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//設置刪除線 
 msp.setSpan(new StrikethroughSpan(), 30, 33, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//設置上下標 
 msp.setSpan(new SubscriptSpan(), 34, 35, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //下標 
 msp.setSpan(new SuperscriptSpan(), 36, 37, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //上標

//超級鏈接(需要添加setMovementMethod方法附加響應) msp.setSpan(new URLSpan("tel:4155551212"), 37, 39, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //電話
TextView.setMovementMethod(LinkMovementMethod.getInstance());

其中:
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE等的作用:
用來標識在 Span 範圍內的文本前後輸入新的字符時是否把它們也應用這個效果。分別有 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前後都不包括)、Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,後面不包括)、Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,後面包括)、Spanned.SPAN_INCLUSIVE_INCLUSIVE(前後都包括)。

那麼,這種方法和上面的方法有什麼區別呢,其實使用上面的那種方法,最後也是一樣的調用第二種方式來格式化的,只不過android系統幫我們解析了html而已。我們使用起來比較方便,但是儘量的使用第二種方法,因爲,性能更高。並且使用第二種方法的時候,還有其他的優勢,就是我們可以自定義span,比如:還是那個需求,再增加一個功能,點擊詳細信息,可以彈出個框,提示用戶一些重要的信息。
我們自定義個span繼承ClickableSpan,URLSpan也是繼承ClickableSpan的:

    private class MyClickPan extends ClickableSpan{

        private final String mURL;

         public MyClickPan(String url){
             mURL = url;
         }

        @Override
        public void onClick(View widget) {

            if(mURL != null){
                Toast.makeText(widget.getContext(),"uri = "+mURL,Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            //取消下劃線
            ds.setUnderlineText(false);
            ds.setColor(Color.RED);
        }
    }

然後再給SpannableString.setPan就可以了:

   SpannableString spannableString = new SpannableString(values[0]);
    spannableString.setSpan(new MyClickPan("Nipuream"), values[0].indexOf("!") + 1, values[0].length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        uber.setText(spannableString);
        uber.setHighlightColor(Color.WHITE);
        uber.setMovementMethod(LinkMovementMethod.getInstance());

這樣就實現了我們的需求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章