編輯:關於Android編程
前些天有個大神告訴我,你寫這麼多TetxtView實際上就實現了一個TextView的功能。
我說我要設置background,textsize,textcolor阿,還有各種點擊事件,一個TextView怎麼能實現呢?
他丟給了我一個TextViewUtil文件,我稍稍看了下,裡面主要用到了 SpannableString 這個神奇的東西。
於是就想深究一下這個SpannableString到底怎麼去使用,都有哪些神奇的功能呢?
一邊學習、一邊寫demo、一邊看效果……
我寫了這麼個工具類,裡面的注釋已經很詳細了(PS:詳細得自己都不高興再寫文字講解了- -):
public class SpannableUtil
{
/**
* 僅實現了大部分效果
* 有些些方法還需調整
* 後面有點偷懶 直接給了構造方法的參數 沒有多寫幾個重載來更好地支持自定義
**/
/**
* 1、BackgroundColorSpan 背景色
2、ClickableSpan 文本可點擊,有點擊事件
3、ForegroundColorSpan 文本顏色(前景色)
4、MaskFilterSpan 修飾效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter) (經測試無效,不知如何生效 硬件加速關閉也沒有效果)
5、MetricAffectingSpan 父類,一般不用
6、RasterizerSpan 光柵效果 (經測試無效,不知如何生效 硬件加速關閉也沒有效果)
7、StrikethroughSpan 刪除線(中劃線)
8、SuggestionSpan 相當於占位符
9、UnderlineSpan 下劃線
10、AbsoluteSizeSpan 絕對大小(文本字體)
11、DynamicDrawableSpan 設置圖片,基於文本基線或底部對齊。
12、ImageSpan 圖片
13、RelativeSizeSpan 相對大小(文本字體)
14、ReplacementSpan 父類,一般不用
15、ScaleXSpan 基於x軸縮放
16、StyleSpan 字體樣式:粗體、斜體等
17、SubscriptSpan 下標(數學公式會用到)
18、SuperscriptSpan 上標(數學公式會用到)
19、TextAppearanceSpan 文本外貌(包括字體、大小、樣式和顏色)
20、TypefaceSpan 文本字體
21、URLSpan 文本超鏈接
**/
/**
* 1
* BackgroundColorSpan 背景色
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param color
* @return
*/
public static SpannableString setBackgroundColorSpan(String content,int startIndex,int endIndex,int color) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new BackgroundColorSpan( color ), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 2
* ClickableSpan 文本可點擊,有點擊事件
* 去下劃線的可點擊span
* @param content
* @param startIndex
* @param endIndex
* @param url
* @return
*/
public static SpannableString setClickableSpan(String content,int startIndex,int endIndex,final String url) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
// super.updateDrawState(ds);
ds.setColor(ds.linkColor);
ds.setUnderlineText(false);
}
@Override
public void onClick(View widget) {
//url處理 跳轉網頁或其他
Uri uri = Uri.parse(url);
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID,
context.getPackageName());
context.startActivity(intent);
Log.i(span, url);
}
}, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 2重載
* ClickableSpan 文本可點擊,有點擊事件
* 去下劃線的可點擊span
* @param content
* @param startIndex
* @param endIndex
* @param clickableSpan 自己重寫ClickableSpan的 updateDrawState與onClick方法
* @return
*/
public static SpannableString setClickableSpan(String content,int startIndex,int endIndex,ClickableSpan clickableSpan) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
if (null != clickableSpan) {
spannableString.setSpan(clickableSpan, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}else {
spannableString.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Log.i(span, clicked);
}
}, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spannableString;
}
/**
* 3
* ForegroundColorSpan 前景色
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param color
* @return
*/
public static SpannableString setForegroundColorSpan(String content,int startIndex,int endIndex,int color) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new ForegroundColorSpan( color ), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 17
* SubscriptSpan 下標(數學公式會用到)
* @param content
* @param startIndex
* @param endIndex
* @return
*/
public static SpannableString setSubscriptSpan( String content, int startIndex, int endIndex ){
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return null;
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan( new SubscriptSpan( ), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE );
return spannableString;
}
/**
* 18
* SuperscriptSpan 上標(數學公式會用到)
* @param content
* @param startIndex
* @param endIndex
* @return
*/
public static SpannableString setSuperscriptSpan( String content, int startIndex, int endIndex ){
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return null;
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan( new SuperscriptSpan( ), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE );
return spannableString;
}
/**
* 4 (經測試無效,不知如何生效 硬件加速關閉也沒有效果)
* MaskFilterSpan 修飾效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter)
* @param content
* @param startIndex
* @param endIndex
* @param blurMaskFilter 模糊
* @param embossMaskFilter 浮雕
* @param maskFilter 其他自定義效果
* @return
*/
public static SpannableString setMaskFilterSpan(String content,int startIndex,int endIndex,
BlurMaskFilter blurMaskFilter,EmbossMaskFilter embossMaskFilter,MaskFilter maskFilter) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
if (null == blurMaskFilter) {
spannableString.setSpan(new BlurMaskFilter(50, Blur.SOLID), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}else {
spannableString.setSpan(blurMaskFilter, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (null == embossMaskFilter) {
spannableString.setSpan(new EmbossMaskFilter(new float[]{1,1,1}, 0.4f, 6, (float)3.5), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}else {
spannableString.setSpan(embossMaskFilter, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (null != maskFilter) {
spannableString.setSpan(maskFilter, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spannableString;
}
/**
* 6 (經測試無效,不知如何生效 硬件加速關閉也沒有效果)
* RasterizerSpan 光柵效果
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @return
*/
public static SpannableString setRasterizerSpan(String content,int startIndex,int endIndex) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new RasterizerSpan(new Rasterizer()), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 7
* StrikethroughSpan 刪除線(中劃線)
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @return
*/
public static SpannableString setStrikethroughSpan(String content,int startIndex,int endIndex) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new StrikethroughSpan(), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 8
* SuggestionSpan 替換建議
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @return
*/
public static SpannableString setSuggestionSpan(String content,int startIndex,int endIndex) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new SuggestionSpan(Locale.CHINESE,new String[]{SuggestionSpan1,SuggestionSpan2,SuggestionSpan3}, SuggestionSpan.FLAG_EASY_CORRECT), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 9
* UnderlineSpan 下劃線
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @return
*/
public static SpannableString setUnderlineSpan(String content,int startIndex,int endIndex) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new UnderlineSpan(), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 10
* AbsoluteSizeSpan 絕對大小(文本字體)(不建議使用,因為此處指定的字號大小以px為單位)
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param size 字號,單位px
* @return
*/
public static SpannableString setAbsoluteSizeSpan(String content,int startIndex,int endIndex,int size) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new AbsoluteSizeSpan(size), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 11
* DynamicDrawableSpan 設置圖片,基於文本基線或底部對齊
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param drawable 顯示的圖片
* @return
*/
public static SpannableString setDynamicDrawableSpan(String content,int startIndex,int endIndex,final Drawable drawable) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
//基線對齊
spannableString.setSpan(new DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BASELINE) {
@Override
public Drawable getDrawable() {
Drawable d = drawable;
d.setBounds(0, 0, 50, 50);
return d;
}
}, startIndex, startIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//底部對齊
spannableString.setSpan(new DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BOTTOM) {
@Override
public Drawable getDrawable() {
Drawable d = drawable;
d.setBounds(0, 0, 50, 50);
return d;
}
}, startIndex + 1, startIndex + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 12
* ImageSpan 圖片
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param drawable 顯示的圖片
* @return
*/
public static SpannableString setImageSpan(String content,int startIndex,int endIndex,final Drawable drawable) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
drawable.setBounds(0, 0, 50, 50);
spannableString.setSpan(new ImageSpan(drawable), startIndex, startIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 13
* RelativeSizeSpan 相對大小(文本字體)
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param size 字縮放倍數
* @return
*/
public static SpannableString setRelativeSizeSpan(String content,int startIndex,int endIndex,float size) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new RelativeSizeSpan(size), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
// ScaleXSpan 基於x軸縮放
/**
* 15
* ScaleXSpan 基於x軸縮放
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param size x軸縮放倍數
* @return
*/
public static SpannableString setScaleXSpan(String content,int startIndex,int endIndex,float size) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new ScaleXSpan(size), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 16
* StyleSpan 字體樣式:粗體、斜體等
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param flag Typeface.xxx
* @return
*/
public static SpannableString setStyleSpan(String content,int startIndex,int endIndex,int flag) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new StyleSpan(flag), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 19
* TextAppearanceSpan 文本字體
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param context
* @return
*/
public static SpannableString setTextAppearanceSpan(String content,int startIndex,int endIndex,Context activity) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new TextAppearanceSpan(activity,android.R.style.TextAppearance_Holo_Large_Inverse), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 20
* TypefaceSpan 文本字體
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param family The font family for this typeface. Examples include
* monospace, serif, and sans-serif.
* @return
*/
public static SpannableString setTypefaceSpan(String content,int startIndex,int endIndex,String family) {
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new TypefaceSpan(family), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
/**
* 21
* URLSpan 文本超鏈接
* @param context
* @param start_index
* @param end_index 設為0則到最後
* @param url
* @return
*/
public static SpannableString setURLSpan( String content, int startIndex, int endIndex, String url ){
if (0 == endIndex) {
endIndex = content.length();
}
if( TextUtils.isEmpty( content ) || startIndex < 0 || endIndex > content.length( ) || startIndex >= endIndex ){
return new SpannableString(content);
}
SpannableString spannableString = new SpannableString( content );
spannableString.setSpan(new URLSpan( url ), startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
}
注意使用了ClickableSpan 點擊事件必須調用TextView的setMovementMethod(LinkMovementMethod.getInstance());方法否則無效
代碼量確實不小= =本來以為只需要幾個小時的 結果花了我昨天一個下午外加今天一個上午 完成demo + 稍微優化。然而這裡面還是有沒有解決的東西 。。。比如 MaskFilterSpan 和RasterizerSpan 這兩個頑疾,在API19下一直都沒跑出來效果,關閉了硬件加速也沒有效果。
如果有大神能夠解決,萬望告知……畢竟覺得MaskFilterSpan 能自定義很多效果,如果不能用的話,癢癢的。
另外,不知各位對Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 這個屬性可有疑問。 我搜索出來有人是這樣說的:
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE — 不包含兩端start和end所在的端點
Spanned.SPAN_EXCLUSIVE_INCLUSIVE — 不包含端start,但包含end所在的端點
Spanned.SPAN_INCLUSIVE_EXCLUSIVE — 包含端start,但不包含end所在的端點
Spanned.SPAN_INCLUSIVE_INCLUSIVE— 包含兩端start和end所在的端點
但我嘗試後好像並不是這樣。
此處引用 http://aichixihongshi.iteye.com/blog/1207503 的文章
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,這是在 setSpan 時需要指定的 flag,它的意義我試了很久也沒試出來,睡個覺,今天早上才突然有點想法,試之,果然。它是用來標識在 Span
范圍內的文本前後輸入新的字符時是否把它們也應用這個效果。分別有
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前後都不包括)、
Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,後面不包括)、
Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,後面包括)、
Spanned.SPAN_INCLUSIVE_INCLUSIVE(前後都包括)。
看個截圖就更明白了:
當了解了Android坐標系和觸控事件後,我們再來看看如何使用系統提供的API來實現動態地修改一個View的坐標,即實現滑動效果。而不管采用哪一種方式,其實現的思想基本是
如果listitem裡面包括button或者checkbox等控件,默認情況下listitem會失去焦點,導致無法響應item的事件,最常用的解決辦法 是在listite
在運行時添加碎片 點擊獲取源碼 將UI分割為多個可配置的部分是碎片的優勢之一,但其真正強大之處在於可在運行時動態地把它們添加到活動中。 1、使用上一篇創建的F
熟悉RxAndroid的使用方法. 要點包含: (1) 鏈式表達式的使用方式. (2) Lambda的應用. (3) Rx處理網絡請求. (4) 線程自動管理, 防止內存