天天看點

一個簡單的Android富文本TextView實作

Android用戶端富文本

現階段問題

在Android用戶端展現一個普通資料非常的友善,直接調用textview.setText()方法,但是當TextView比較複雜時(例如存在@使用者,##話題,部分字元樣式、網址連結等),普通的TextView就無法完成需求,需要自己封裝一個富文本TextView。

Demo

Coding 冒泡示例

##

分析

1.一個TextView中不同類别的資訊,需要由不同樣式的顯示,一般的用法textview.setTextColor(color)會将所有的文字變成同一種顔色,這顯然是不符合要求的。為了實作這一個需求,我們将會用到SpannableStringBuilder這個類。

預備知識

SpannableStringBuilder:

基本概念

SpannableStringBuilder 和 StringBuilder類似,都可以存儲字元串,不同的是SpannableStringBuilder有一個setSpan()函數,可以給存儲的String添加不同的樣式。如加下劃線、背景色、字型顔色、字型大小等。

另外需要注意的是,當SpannableStringBuilder中存儲了一個有樣式的String,當把spannableStringBuilder展示在TextView、EditTextView中時,能顯示這些樣式;當展示在canvas上時,因為Canvas不支援SpannableStringBuilder的額外資訊,是以會退化成一個普通的String,不顯示樣式資訊。

setSpan()函數

void setSpan(Object what,int startIndex,int endIndex,int flag);

說明:

參數

說明

Object what

設定Span樣式

int startIndex

樣式開始的Index

int endIndex

樣式結束的Index

int flag

新插入字元的樣式設定

注意點:

endIndex:字型樣式結束的Index,該Index對應的字元不使用樣式,比如有一個字元串為s = “abcd”,s.setSpan(span,0,2,flag),此時第0、1個字元ab使用了樣式span,endIndex對應的字元c不使用。

flag:取值如下

取值

Spannable.SPAN_EXCLUSIVE_EXCLUSIVE

前後都不包括,即在指定範圍的前面和後面插入新字元都不會應用新樣式

Spannable.SPAN_EXCLUSIVE_INCLUSIVE

前面不包括,後面包括。即僅在範圍字元的後面插入新字元時會應用新樣式

Spannable.SPAN_INCLUSIVE_EXCLUSIVE

前面包括,後面不包括。

Spannable.SPAN_INCLUSIVE_INCLUSIVE

前後都包括

簡單示例

1

2

3

4

5

6

//設定字型顔色

textview1 = (TextView) findViewById(R.id.text1);

SpannableStringBuilder spannableStringBuilder1 = newSpannableStringBuilder("Android");

ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.BLUE);

spannableStringBuilder1.setSpan(foregroundColorSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview1.setText(spannableStringBuilder1);

效果:

多種Span

由以上的簡單示例我們可以看出,設定一個樣式的一般步驟是:

構造一個SpannableStringBuilder

構造一個Span,并設定在SpannableStringBuilder上

将SpannableStringBuilder綁定在TextView上展示

設定字型顔色

這個已經在簡單示例中展示

設定字型背景顔色

//設定字型背景顔色

textview2 = (TextView) findViewById(R.id.text2);

SpannableStringBuilder spannableStringBuilder2 = newSpannableStringBuilder("Android");

BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(Color.RED);

spannableStringBuilder2.setSpan(backgroundColorSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview2.setText(spannableStringBuilder2);

設定字型大小

//設定字型大小

textview3 = (TextView) findViewById(R.id.text3);

SpannableStringBuilder spannableStringBuilder3 = newSpannableStringBuilder("Android");

AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(30);

spannableStringBuilder3.setSpan(absoluteSizeSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview3.setText(spannableStringBuilder3);

設定字型

//設定字型(加粗斜體)

textview4 = (TextView) findViewById(R.id.text4);

SpannableStringBuilder spannableStringBuilder4 = newSpannableStringBuilder("Android");

StyleSpan styleSpan = new StyleSpan(Typeface.BOLD_ITALIC);

spannableStringBuilder4.setSpan(styleSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview4.setText(spannableStringBuilder4);

設定下劃線

//設定下劃線

textview5 = (TextView) findViewById(R.id.text5);

SpannableStringBuilder spannableStringBuilder5 = newSpannableStringBuilder("Android");

UnderlineSpan underlineSpan = new UnderlineSpan();

spannableStringBuilder5.setSpan(underlineSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview5.setText(spannableStringBuilder5);

//設定删除線

textview6 = (TextView) findViewById(R.id.text6);

SpannableStringBuilder spannableStringBuilder6 = newSpannableStringBuilder("Android");

StrikethroughSpan strikethroughSpan = new StrikethroughSpan();

spannableStringBuilder6.setSpan(strikethroughSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview6.setText(spannableStringBuilder6);

設定多種樣式

除了設定一個樣式以外,SpannableStringBuilder還支援設定多個樣式。

7

8

9

//設定多種樣式

textview7 = (TextView) findViewById(R.id.text7);

SpannableStringBuilder spannableStringBuilder7 = newSpannableStringBuilder("Android");

spannableStringBuilder7.setSpan(foregroundColorSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

spannableStringBuilder7.setSpan(backgroundColorSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

spannableStringBuilder7.setSpan(underlineSpan, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

spannableStringBuilder7.setSpan(absoluteSizeSpan, 3, 6, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

spannableStringBuilder7.setSpan(strikethroughSpan, 3, 6, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);

textview7.setText(spannableStringBuilder7);

點選事件

執行個體代碼

10

11

12

13

14

15

16

17

18

SpannableStringBuilder spannableStringBuilder = newSpannableStringBuilder("Android");

spannableStringBuilder.setSpan(

    new ClickableSpan() {

        @Override

        public void onClick(View widget) {

            //do something

        }

        public void updateDrawState(TextPaint ds) {

            //設定一些樣式

            //ds.setUnderlineText(false);

            //ds.setColor(color);

    }, startIndex, endIndex,

    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE

);

簡單實作

以Listview為例,我們需要實作的是每一個Item和Adapter中的getView()函數。

布局檔案

19

20

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent" android:layout_height="wrap_content">

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@mipmap/ic_launcher"

        android:layout_marginTop="16dp"

        android:layout_marginLeft="16dp"

        android:id="@+id/userhead"/>

    <TextView

        android:layout_width="match_parent"

        android:layout_marginLeft="8dp"

        android:layout_marginRight="16dp"

        android:layout_toRightOf="@id/userhead"

        android:id="@+id/content"/>

</RelativeLayout>

ImageView表示該Item的釋出人,和富文本沒有關系,在Demo中直接寫死資料。

主要内容在TextView中

因為每一個TextView中可能有多個不同樣式的文本(#話題#,@其他使用者,文本内容等),是以先封裝一個文本類:

public class TextObject {

    private int mStartIndex;//樣式的開始字元

    private int mEndIndex;//結束字元

    //private SpannableStringBuilder mSpannableStringBuilder;

    private String content;//文本内容

    private int mOptionType;//操作符

    private int color;//字型顔色

    private boolean isUnderline;//是否有下劃線

    //...其他需要的屬性可繼承TextObject自行添加

  }

這個類封裝的是一個Item中的某一個文本樣式,是以我們還需要封裝一個Item中的文本類:

public class Content {

    private List<TextObject> mList;

    private SpannableStringBuilder mSpannableStringBuilder;

這個類中有一個List成員變量,存放着該Item中所有的樣式文本。

對于成員變量SpannableStringBuilder,有一個init函數,用于根據List中的TextObject拼接StringBuilder

//拼接SpannableStringBuilder

public void initStringBuilder(){

    SpannableStringBuilder spannableStringBuilder = newSpannableStringBuilder();

    for (int i = 0;i<mList.size();i++){

        spannableStringBuilder.append(mList.get(i).getContent());

    }

    setmSpannableStringBuilder(spannableStringBuilder);

}

到現在,資料準備工作已經全部完成,現在就要顯示這些資料了。

getView()函數

getview函數中就可以通過content.getSpannableStringBuilder()函數擷取到正文内容,然後即可根據上面的預備知識對其進行設定不同的樣式和點選事件。

設定完成後,需要調用

viewHolder.textview.setMovementMethod(LinkMovementMethod.getInstance());

使其響應點選事件。

Demo效果:

Demo位址

<a href="https://github.com/basti-shi031/RichTextView">Github項目位址</a>

    本文轉自 一點點征服   部落格園部落格,原文連結:http://www.cnblogs.com/ldq2016/p/6122393.html,如需轉載請自行聯系原作者

繼續閱讀