Android中各種Span的用法

在安卓開發(fā)中,我們經(jīng)常會(huì)遇到給一個(gè)TextView中的部分文字設(shè)置不同的樣式的情況,這之后我們不可能總是用好幾個(gè)TextView來拼出這樣一個(gè)效果,此時(shí),我們就需要用到'''SpannableString'''以及和各種'''Span'''的用法了,下面就讓我們看一下Android給我們提供了多少種Span,以及各自的用法:

SpannableString

首先來介紹SpannableString,官方解釋是:這是內(nèi)容不可變但可以附加和分離標(biāo)記對(duì)象的文本的類。所謂的標(biāo)記對(duì)象就是我們接下來要介紹的Span。對(duì)于他的用法:

SpannableString string = new SpannableString(str);
//設(shè)置TextView,可以被當(dāng)做字符串設(shè)置給TextView
textView.setText(string);

各種Span

  • BackgroundColorSpan:給部分文字設(shè)置背景顏色
  • ForegroundColorSpan:給部分文字設(shè)置前景色
  • ClickableSpan:設(shè)置點(diǎn)擊事件
  • URLSpan:設(shè)置鏈接,相當(dāng)于Html的標(biāo)簽
  • MaskFilterSpan:文字的裝飾效果。分為兩種:BlurMaskFilter(模糊效果) 和 EmbossMaskFilter (浮雕效果)
  • AbsoluteSizeSpan:設(shè)置字體大小
  • RelativeSizeSpan:設(shè)置字體的相對(duì)大小
  • ImageSpan:設(shè)置圖片
  • ScaleXSpan:橫向壓縮
  • SubscriptSpan:設(shè)置腳注
  • SuperscriptSpan:上標(biāo),相當(dāng)于數(shù)學(xué)中的平方樣式
  • TextAppearanceSpan:使用style來定義文本樣式
  • TypefaceSpan:設(shè)置字體
  • RasterizerSpan:設(shè)置光柵字樣
  • StrikethroughSpan:刪除線,相當(dāng)于購(gòu)物網(wǎng)站上的劃掉的原價(jià)
  • UnderlineSpan:下劃線。

以上都是Android源碼提供的效果,下面來大概寫一下用法,其實(shí)用法基本上都是一樣的:

String str = "大家好,下面來演示一下Span的用法";
SpannableString string = new SpannableString(str);
BackgroundColorSpan span = new BackgroundColorSpan(getResources().getColor(R.color.red));
//最后一句就是給String設(shè)置效果的
//第2個(gè)參數(shù)表示從哪個(gè)字符開始設(shè)置背景色
//第3個(gè)參數(shù)表示到哪個(gè)字符結(jié)束背景色的設(shè)置
//最后一個(gè)參數(shù)表示是否包含所設(shè)置的第二、三參數(shù)所代表的位置
//本例中設(shè)置的是前后都不包括
//相同的還有:
//Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,后面不包括)、
//Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,后面包括)、
//Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)
string.setSpan(span,0,3,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

其實(shí)看著android的文檔,或者上網(wǎng)上搜這些東西會(huì)有很多,大家可以跟著敲一遍代碼,運(yùn)行一下就能清楚地知道每個(gè)Span所代表的含義了。下面進(jìn)入主題:

公司最近要做一個(gè)練習(xí)的需求,就相當(dāng)于學(xué)生在App中做試卷似得,于是就有了下面的這個(gè)設(shè)計(jì)圖:


QQ20170115-0@2x.png

大概就是這種樣式的圖片,一段文字里面會(huì)嵌入很多的空格,并且還有背景色,還有文字,而且點(diǎn)擊的時(shí)候還有各種效果,為了實(shí)現(xiàn)這個(gè)功能,確實(shí)看了很多Span的樣式。

剛開始的時(shí)候準(zhǔn)備用BackgroundColorSpan+ClickableSpan來實(shí)現(xiàn)這樣的功能,后來發(fā)現(xiàn),BackgroundColorSpan不能完美的實(shí)現(xiàn)這個(gè)功能,于是在網(wǎng)上找了BackgroundImageSpan來實(shí)現(xiàn)的。

public class BackgroundImageSpan extends ReplacementSpan implements ParcelableSpan {
  private static final String TAG = "BackgroundImageSpan";
  private Drawable mDrawable;
  private int mImageId;
  private int mWidth = -1;
 /**
 * new BackgroundImageSpan use resource id and Drawable
 * @param id the drawable resource id
 * @param drawable Drawable related to the id
 * @internal
 * @hide
 */
 public BackgroundImageSpan(int id, Drawable drawable) {
   mImageId = id;
   mDrawable = drawable;
 }
 /**
 * @hide
 * @internal
 */
 public BackgroundImageSpan(Parcel src) {
   mImageId = src.readInt();
 }
 /**
 * @hide
 * @internal
 */
 public void draw(Canvas canvas, int width,float x,int top, int y, int bottom, Paint paint) {
  if (mDrawable == null) {//if no backgroundImage just don't do any draw
    Log.e(TAG, "mDrawable is null draw()");
    return;
 }
   Drawable drawable = mDrawable;
   canvas.save();
   canvas.translate(x, top); // translate to the left top point
   mDrawable.setBounds(0, 0, width, (bottom - top));
   drawable.draw(canvas);
   canvas.restore();
 }
 @Override
 public void updateDrawState(TextPaint tp) {
 }
 /**
 * return a special type identifier for this span class
 * @hide
 * @internal
 * @Override
 */
 public int getSpanTypeId() {
    return 0;
 }
 /**
 * describe the kinds of special objects contained in this Parcelable's marshalled representation
 * @hide
 * @internal
 * @Override
 */
 public int describeContents() {
   return 0;
 }
 /**
 * flatten this object in to a Parcel
 * @hide
 * @internal
 * @Override
 */
 public void writeToParcel(Parcel dest, int flags) {
   dest.writeInt(mImageId);
 }
 /**
 * @hide
 * @internal
 */
 public void convertToDrawable(Context context) {
   if (mDrawable == null) {
     mDrawable = context.getResources().getDrawable(mImageId);
   }
 }
 /**
 * convert a style text that contain BackgroundImageSpan, Parcek only pass resource id,
 * after Parcel, we need to convert resource id to Drawable.
 * @hide
 * @internal
 */
 public static void convert(CharSequence text , Context context) {
   if (!(text instanceof SpannableStringBuilder)) {
     return;
   }
   SpannableStringBuilder builder = (SpannableStringBuilder)text;
   BackgroundImageSpan[] spans = builder.getSpans(0, text.length(),     BackgroundImageSpan.class);
   if (spans == null || spans.length == 0) {
      return;
   }
   for (int i = 0; i < spans.length; i++) {
     spans[i].convertToDrawable(context);
   }
 }
 /**
 * draw the span
 * @hide
 * @internal
 * @Override
 */
 public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
   // draw image
   draw(canvas, mWidth,x,top, y, bottom, paint);
   // draw text
   // the paint is already updated
   canvas.drawText(text,start,end, x,y, paint);
 }
 /**
 * get size of the span
 * @hide
 * @internal
 * @Override
 */
 public int getSize(Paint paint, CharSequence text, int start, int end,
 FontMetricsInt fm) {
   float size = paint.measureText(text, start, end);
   if (fm != null && paint != null) {
     paint.getFontMetricsInt(fm);
   }
   mWidth = (int)size;
   return mWidth;
 }
}

以上這段代碼是谷歌的Git上提供的,最后會(huì)放上鏈接,接著說一下我實(shí)現(xiàn)上圖的過程,上代碼比較快:

//前兩行使用這則表達(dá)式來解析出一大段文本里面需要特殊設(shè)置的文本內(nèi)容
Matcher textMatcher;
SpannableString str = new SpannableString(contentStr);
textMatcher = PATTERN_TEXT_SRC.matcher(content);
while (textMatcher.find()) {  
//然后開始循環(huán)所有的文本來進(jìn)行設(shè)置。  
    final String blank = textMatcher.group().trim();
    int index = content.indexOf(blank);
    ClickableSpan clickableSpan = new MyClickableSpan();    
    BackgroundImageSpan backgroundColorSpan;
    //此處之所以有這個(gè)判斷,完全是為了用來設(shè)置點(diǎn)擊時(shí)候的樣式,而currentStart和currentEnd也是點(diǎn)擊的文字的第一字符的position和最后一個(gè)字符的position
    if (currentStart == index && currentEnd == index + blank.length()) {        
       backgroundColorSpan = new BackgroundImageSpan(R.drawable.bg_answer_wrong, getResources().getDrawable(R.drawable.bg_answer_wrong));
    } else { 
       backgroundColorSpan = new BackgroundImageSpan(R.drawable.bg_noanswer_unselected, getResources().getDrawable(R.drawable.bg_noanswer_unselected));    
}
    str.setSpan(backgroundColorSpan, index, index + blank.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);      
    str.setSpan(clickableSpan, index, index + blank.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}
    //如果要設(shè)置點(diǎn)擊事件,那么久一低昂要設(shè)置下面這一行,否則點(diǎn)擊事件不起作用
    mTextView.setMovementMethod(LinkMovementMethod.getInstance());
    mTextView.setText(str);

//此處是定義的ClickableSpan
private class MyClickableSpan extends ClickableSpan {
    @Override
    public void onClick(View widget) {
        //這里面的代碼主要是用來獲取所點(diǎn)擊的那一部分的text,也可以根據(jù)下面的方法來獲取具體點(diǎn)擊的那部分文字內(nèi)容
        TextView tv = (TextView) widget;
        Spanned s = (Spanned) tv.getText();
        int start = s.getSpanStart(this);
        int end = s.getSpanEnd(this); 
        currentStart = start;
        currentEnd = end;
        updateBlank();
        Toast.makeText(MainActivity.this, tv.getText(), Toast.LENGTH_SHORT).show();    }}

基本上就是這些內(nèi)容了,其實(shí)做起來不太難,主要是各種狀態(tài)判斷起來比較崩潰,下面一些就是收集個(gè)各種需要的效果:

給所設(shè)置的部分內(nèi)容添加padding

BackgroundImageSpan

Clickable獲取點(diǎn)擊的文本內(nèi)容

ClickableSpan的一些樣式的設(shè)置

基本上就是以上這么多了,謝謝這些鏈接的文章作者所提供的優(yōu)質(zhì)內(nèi)容,權(quán)當(dāng)記錄了吧。

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

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

  • 前言 工作找完了,已經(jīng)干了兩個(gè)星期。雖然經(jīng)常加班,不過相比之前的工作,現(xiàn)在過得更加充實(shí)、更有意義?,F(xiàn)在有點(diǎn)空閑時(shí)間...
    帶心情去旅行閱讀 73,795評(píng)論 42 237
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,516評(píng)論 19 139
  • CSS基礎(chǔ) 本文包括CSS基礎(chǔ)知識(shí)選擇器(重要?。。。├^承、特殊性、層疊、重要性CSS格式化排版單位和值盒模型浮動(dòng)...
    廖少少閱讀 3,423評(píng)論 0 40
  • 選擇qi:是表達(dá)式 標(biāo)簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font,text-align,li...
    wzhiq896閱讀 2,103評(píng)論 0 2
  • 兩天的出差,有些累累了。突然的降溫,外面冷,車?yán)餆?,溫度差總?huì)讓人身體起變化。天已黑看不出車窗外的景色,只有些瑣碎...
    WoodSage閱讀 215評(píng)論 2 1

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