Android 富文本裝飾器Spannable

1、Spannable簡介

Android中爲了讓TextView實現富文本信息來簡化實現某些特定的樣式,須要藉助Spannable的子類或者SpannableStringBuilder實現。html

在Android中最原始的顯示富文本的方式是使用 Html工具類 或者android:autolink,但這兩種方式顯示的樣式並不理想,所以,深層次的java

修改樣式是必須的android

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:autoLink="all"
            android:text="@string/link_text_auto"
            />

 

TextView t3 = (TextView) findViewById(R.id.text3);
t3.setText(
    Html.fromHtml(
         "<b>text3:</b>  Text with a " +
         "<a href=\"http://www.google.com\">link</a> " +
         "created in the Java source code using HTML."));
t3.setMovementMethod(LinkMovementMethod.getInstance()); //用來決定是否觸發超連接點擊事件

 

Spannable是一個接口,他有一個子類是SpannableStringweb

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/link_text_auto"
            />

 

SpannableString ss = new SpannableString("text4: Click here to dial the phone.");
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 6,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new URLSpan("tel:4155551212"), 13, 17,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

TextView t4 = (TextView) findViewById(R.id.text4); //注意,沒有android:autolink屬性
t4.setText(ss);
t4.setMovementMethod(LinkMovementMethod.getInstance());

 

1.setMovementMethod,此方法在須要響應用戶事件時使用,如點擊一個電話號碼就跳轉到撥號頁面。
若是不執行這個方法是不會響應事件的,即使文本看着已是下劃線藍色字了。 

2.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,這是在 setSpan 時須要指定的 flag,
它是用來標識在 Span 範圍內的文本先後輸入新的字符時是否把它們也應用這個效果。

分別有 
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(先後都不包括)、
Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,後面不包括)、
Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,後面包括)、
Spanned.SPAN_INCLUSIVE_INCLUSIVE(先後都包括)。看個截圖就更明白了:

 

2、代碼實踐

其實設置樣式的有不少,另外須要特別指出的3個Spannable樣式是ClickSpan和ImageSpan,UrlSpan是目前app中經常使用的Spannable樣式之一,如QQ圖文等,先來看下面的代碼網絡

import java.io.IOException;

import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.content.res.ColorStateList;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.DrawableMarginSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.IconMarginSpan;
import android.text.style.ImageSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.ScaleXSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.TextAppearanceSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.widget.TextView;

public class TextViewLinkActivity extends Activity {
    TextView mTextView = null;   
    SpannableString msp = null;  
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mTextView = (TextView)findViewById(R.id.myTextView);
        
        //建立一個 SpannableString對象  
        msp = new SpannableString("字體測試字體大小一半兩倍前景色背景色正常粗體斜體粗斜體下劃線刪除線x1x2電話郵件網站短信彩信地圖X軸綜合/bot"); 
        
        //設置字體(default,default-bold,monospace,serif,sans-serif)
        msp.setSpan(new TypefaceSpan("monospace"), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        msp.setSpan(new TypefaceSpan("serif"), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        //設置字體大小(絕對值,單位:像素) 
        msp.setSpan(new AbsoluteSizeSpan(20), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        msp.setSpan(new AbsoluteSizeSpan(20,true), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  //第二個參數boolean dip,若是爲true,表示前面的字體大小單位爲dip,不然爲像素,同上。
        
        //設置字體大小(相對值,單位:像素) 參數表示爲默認字體大小的多少倍
        msp.setSpan(new RelativeSizeSpan(0.5f), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  //0.5f表示默認字體大小的一半
        msp.setSpan(new RelativeSizeSpan(2.0f), 10, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  //2.0f表示默認字體大小的兩倍
        
        //設置字體前景色
        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 StyleSpan(android.graphics.Typeface.BOLD), 20, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  //粗體
        msp.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 22, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  //斜體
        msp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD_ITALIC), 24, 27, 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);     //電話   
        msp.setSpan(new URLSpan("mailto:webmaster@google.com"), 39, 41, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);     //郵件   
        msp.setSpan(new URLSpan("http://www.baidu.com"), 41, 43, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);     //網絡   
        msp.setSpan(new URLSpan("sms:4155551212"), 43, 45, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);     //短信   使用sms:或者smsto:
        msp.setSpan(new URLSpan("mms:4155551212"), 45, 47, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);     //彩信   使用mms:或者mmsto:
        msp.setSpan(new URLSpan("geo:38.899533,-77.036476"), 47, 49, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);     //地圖   
        
        //設置字體大小(相對值,單位:像素) 參數表示爲默認字體寬度的多少倍
        msp.setSpan(new ScaleXSpan(2.0f), 49, 51, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //2.0f表示默認字體寬度的兩倍,即X軸方向放大爲默認字體的兩倍,而高度不變
        
        //設置字體(依次包括字體名稱,字體大小,字體樣式,字體顏色,連接顏色)
        ColorStateList csllink = null;
        ColorStateList csl = null;
        XmlResourceParser xppcolor=getResources().getXml (R.color.color);
        try {
        	csl= ColorStateList.createFromXml(getResources(),xppcolor);
        }catch(XmlPullParserException e){
        	// TODO: handle exception
        	e.printStackTrace();    	
        }catch(IOException e){
        	// TODO: handle exception
        	e.printStackTrace();    	
        }

        XmlResourceParser xpplinkcolor=getResources().getXml(R.color.linkcolor);
        try {
        	csllink= ColorStateList.createFromXml(getResources(),xpplinkcolor);
        }catch(XmlPullParserException e){
        	// TODO: handle exception
        	e.printStackTrace();    	
        }catch(IOException e){
        	// TODO: handle exception
        	e.printStackTrace();    	
        }
        msp.setSpan(new TextAppearanceSpan("monospace",android.graphics.Typeface.BOLD_ITALIC, 30, csl, csllink), 51, 53, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
     
        //設置項目符號
        msp.setSpan(new BulletSpan(android.text.style.BulletSpan.STANDARD_GAP_WIDTH,Color.GREEN), 0 ,msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //第一個參數表示項目符號佔用的寬度,第二個參數爲項目符號的顏色

        //設置圖片
        Drawable drawable = getResources().getDrawable(R.drawable.icon); 
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());  
        msp.setSpan(new ImageSpan(drawable), 53, 57, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        mTextView.setText(msp);
        mTextView.setMovementMethod(LinkMovementMethod.getInstance()); 
    }
}

 

 

從這段代碼能夠看出,幾乎全部的樣式都在下面的包中,其中最原始的是CharacterStyle類,他直接影響着圖形的繪製工具Paint類。app

android.text.style

 

Spannable spannable = new SpannableString(src);
		spannable.setSpan(new CharacterStyle() {
			
		@Override
	        public void updateDrawState(TextPaint tp) {  //所欲的Span類都繼承了這個方法,因此神馬下劃線啊,字體啊,加粗啊可使用
				tp.setColor(0xfff02828);
			}
		}, 0, src.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		
	        spannable.setSpan(new ClickableSpan() {
			
			@Override
			public void onClick(View widget) {
			    //to do something
			}
		},0, src.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

 

來看看URLSpan吧,URLSpan繼承自Clickable,onclick方法以下dom

  @Override
    public void onClick(View widget) {
        Uri uri = Uri.parse(getURL()); 
        Context context = widget.getContext();
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
        context.startActivity(intent);
    }

 

再來看看ImageSpanide

  //  根據隨機產生的1至9的整數從R.drawable類中得到相應資源ID(靜態變量)的Field對象
        Field field = R.drawable.class.getDeclaredField("face" + randomId);
        //  得到資源ID的值,也就是靜態變量的值
        int resourceId = Integer.parseInt(field.get(null).toString());
        //  根據資源ID得到資源圖像的Bitmap對象
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
        //  根據Bitmap對象建立ImageSpan對象
        ImageSpan imageSpan = new ImageSpan(this, bitmap);  //這種構建方法不太經常使用
        //  建立一個SpannableString對象,以便插入用ImageSpan對象封裝的圖像
        SpannableString spannableString = new SpannableString("face");
        //  用ImageSpan對象替換face
        spannableString.setSpan(imageSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        //  將隨機得到的圖像追加到EditText控件的最後
        edittext.append(spannableString);

 

再來看看繼承樹工具

 

     

 

SpannableStringBuilder,和StringBuilder相似,效率更高,消耗內存少;Spannable與String相似,是常量,沒法修改。測試

3、URLSpan實現富文本交互

href+URLSpan處理一段內容中屢次出現超連接的數據

StringBuilder actionText = new StringBuilder(''); 
actionText.append("<a style=\"color:blue;text-decoration:none;\" href='notesClause'>click Me</a>");
orderdetailclauseTV.setText(Html.fromHtml(actionText.toString())); //html內容處理
orderdetailclauseTV.setMovementMethod(LinkMovementMethod.getInstance());

Spannable spannable = (Spannable) orderdetailclauseTV.getText();
URLSpan[] urlspan = spannable.getSpans(0, ends, URLSpan.class); //獲取全部超連接href的屬性值

SpannableStringBuilder stylesBuilder = new SpannableStringBuilder(text);

tylesBuilder.clearSpans(); // should clear old spans

for (URLSpan url : urlspan) 
{
	TextViewURLSpan myURLSpan = new TextViewURLSpan(url.getURL());
	stylesBuilder.setSpan(myURLSpan}, spannable.getSpanStart(url),spannable.getSpanEnd(url),spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private class TextViewURLSpan extends ClickableSpan {
		private String clickString;

		public TextViewURLSpan(String clickString) {
			this.clickString = clickString;
		}

		@Override
		public void updateDrawState(TextPaint ds) {
			ds.setColor(OrderDetail.this.getResources().getColor(
					R.color.red_text_color));
			ds.setUnderlineText(false); // 去掉下劃線
		}

		@Override
		public void onClick(View widget) {
		    if(clickString.equal('notesClause')) //匹配href屬性的值
		   {
		     startActivity(NetActivity.class);
		   }
		}
}

 

try  doing it;

相關文章
相關標籤/搜索