一、概述
1、SpannableString、SpannableStringBuilder与String的关系
首先SpannableString、SpannableStringBuilder基本上与String差不多,也是用来存储字符串,但它们俩的特殊就在于有一个SetSpan()函数,能给这些存储的String添加各种格式或者称样式(Span),将原来的String以不同的样式显示出来,比如在原来String上加下划线、加背景色、改变字体颜色、用图片把指定的文字给替换掉,等等。所以,总而言之,SpannableString、SpannableStringBuilder与String一样, 首先也是传字符串,但SpannableString、SpannableStringBuilder可以对这些字符串添加额外的样式信息,但String则不行。
注意:如果这些额外信息能被所用的方式支持,比如将SpannableString传给TextView;也有对这些额外信息不支持的,比如前一章讲到的Canvas绘制文字,对于不支持的情况,SpannableString和SpannableStringBuilder就是退化为String类型,直接显示原来的String字符串,而不会再显示这些附加的额外信息。
2、SpannableString与SpannableStringBuilder区别
它们的区别在于 SpannableString像一个String一样,构造对象的时候传入一个String,之后再无法更改String的内容,也无法拼接多个 SpannableString;而SpannableStringBuilder则更像是StringBuilder,它可以通过其append()方法来拼接多个String:
//使用SpannableString,必须一次传入,构造完成
SpannableString word = new SpannableString("欢迎光临Harvic的博客");
//使用SpannableStringBuilder,可以使用append()再添加
SpannableStringBuilder multiWord = new SpannableStringBuilder();
multiWord.append("欢迎光临");
multiWord.append("Harvic的");
multiWord.append("博客");
这是相关的API的Class General Hierarchy:
因为Spannable等最终都实现了CharSequence接口,所以可以直接把SpannableString和
SpannableStringBuilder通过TextView.setText()设置给TextView。
3、SetSpan()
void setSpan (Object what, int start, int end, int flags)
函数意义:给SpannableString或SpannableStringBuilder特定范围的字符串设定Span样式,可以设置多个(比如同时加上下划线和删除线等),Falg参数标识了当在所标记范围前和标记范围后紧贴着插入新字符时的动作,即是否对新插入的字符应用同样的样式。(这个后面会具体举例说明)
参数说明:
object what :对应的各种Span,后面会提到;
int start:开始应用指定Span的位置,索引从0开始
int end:结束应用指定Span的位置,特效并不包括这个位置。比如如果这里数为3(即第4个字符),第4个字符不会有任何特效。从下面的例子也可以看出来。
int flags:取值有如下四个
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
Spannable.SPAN_EXCLUSIVE_INCLUSIVE :前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
Spannable.SPAN_INCLUSIVE_EXCLUSIVE :前面包括,后面不包括。
Spannable.SPAN_INCLUSIVE_INCLUSIVE :前后都包括。
举个例子来说明这个前后包括的问题:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.try_spannable_blog.MainActivity" >
<EditText
android:id="@+id/edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
这里用一个改变字体颜色的Span来做下演示
public class MainActivity extends Activity {
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText)findViewById(R.id.edit);
//改变字体颜色
//先构造SpannableString
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
//再构造一个改变字体颜色的Span
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
//将这个Span应用于指定范围的字体
spanString.setSpan(span, 1, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
//设置给EditText显示出来
editText.setText(spanString);
}
}
初始化效果是这样的:
分别在设置Span的前面和后面加入新文字,结果是这样的
在前面和后面都加入虾米两个字,可见,前面的虾米没有任何效果,后面的则不同,添加上相同的Span特效,这是由于我们设置了Spannable.SPAN_EXCLUSIVE_INCLUSIVE的原因,即(前面不应用特效,后面应用特效),其它几个Flags参数的含义想必大家也都清楚了。在此就不再赘述。
二、各种Span设置
- AbsoluteSizeSpan(int size) ---- 设置字体大小,参数是绝对数值,相当于Word中的字体大小
- RelativeSizeSpan(float proportion) ---- 设置字体大小,参数是相对于默认字体大小的倍数,比如默认字体大小是x, 那么设置后的字体大小就是x*proportion,这个用起来比较灵活,proportion>1就是放大(zoom in), proportion<1就是缩小(zoom out)
- ScaleXSpan(float proportion) ---- 缩放字体,与上面的类似,默认为1,设置后就是原来的乘以proportion,大于1时放大(zoon in),小于时缩小(zoom out)
- BackgroundColorSpan(int color) ----背景着色,参数是颜色数值,可以直接使用android.graphics.Color里面定义的常量,或是用Color.rgb(int, int, int)
- ForegroundColorSpan(int color) ----前景着色,也就是字的着色,参数与背景着色一致
- TypefaceSpan(String family) ----字体,参数是字体的名字比如“sans", "sans-serif"等
- StyleSpan(Typeface style) -----字体风格,比如粗体,斜体,参数是android.graphics.Typeface里面定义的常量,如Typeface.BOLD,Typeface.ITALIC等等。
- StrikethroughSpan----如果设置了此风格,会有一条线从中间穿过所有的字,就像被划掉一样
3.Linkify
另外,也可以对通过TextView.setAutoLink(int)设置其Linkify属性,其用处在于,TextView会自动检查其内容,会识别出phone number, web address or email address,并标识为超链接,可点击,点击后便跳转到相应的应用,如Dialer,Browser或Email。Linkify有几个常用选项,更多的请参考文档:
- Linkify.EMAIL_ADDRESS -- 仅识别出TextView中的Email在址,标识为超链接,点击后会跳到Email,发送邮件给此地址
- Linkify.PHONE_NUMBERS -- 仅识别出TextView中的电话号码,标识为超链接,点击后会跳到Dialer,Call这个号码
- Linkify.WEB_URLS-- 仅识别出TextView中的网址,标识为超链接,点击后会跳到Browser打开此URL
- Linkify.ALL -- 这个选项是识别出所有系统所支持的特殊Uri,然后做相应的操作
在前面的一个小示例,大家应该也可以看出,要应用一个Span总共分三步:
1、构造String
2、构造Span
3、利用SetSpan()对指定范围的String应用这个Span
1、字体颜色设置(ForegroundColorSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
//再构造一个改变字体颜色的Span
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
//将这个Span应用于指定范围的字体
spanString.setSpan(span, 1, 5, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
//设置给EditText显示出来
editText.setText(spanString);
2、字体背景颜色(BackgroundColorSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
BackgroundColorSpan span = new BackgroundColorSpan(Color.YELLOW);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);
3、字体大小(AbsoluteSizeSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
AbsoluteSizeSpan span = new AbsoluteSizeSpan(16);
spanString.setSpan(span, 2, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
editText.setText(spanString);
4、粗体、斜体(StyleSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC);
spanString.setSpan(span, 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);
5、删除线(StrikethroughSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
StrikethroughSpan span = new StrikethroughSpan();
spanString.setSpan(span, 2, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);
6、下划线(UnderlineSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
UnderlineSpan span = new UnderlineSpan();
spanString.setSpan(span, 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);
7、图片置换(ImageSpan)
ImagSpan有很多构造函数,一般是通过传入Drawableg来构造,详细的构造说明看这里:http://developer.android.com/reference/android/text/style/ImageSpan.html
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
Drawable d = getResources().getDrawable(R.drawable.ic_launcher);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
spanString.setSpan(span, 2, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);
8.电话,邮件等
final TextView textClickable = (TextView) findViewById(R.id.text_view_font_5);
final String contact = "Email: mvp@microsoft.com\n" +
"Phone: +47-24885883\n" +
"Fax: +47-24885883\n" +
"HTTP: www.microsoft.com/mvp.asp";
// Set the attribute first, then set the text. Otherwise, it won't work
textClickable.setAutoLinkMask(Linkify.ALL); // or set 'android:autoLink' in layout xml
textClickable.setText(contact);
转自博客:http://blog.youkuaiyun.com/fengkuanghun/article/details/7904284
最后的总结:
个人认为软件开发中最常见的问题不是某个技巧怎么使用的问题,而是何时该使用何技巧的问题,因为实现同一个目标可能有N种不同的方法,就要权衡利弊,选择最合适的一个,正如常言所云,没有最好的,只有最适合的。如前面所讨论的,要想用不同的字体展现不同的信息可能的解法,除了用Style Span外还可以用多个TextView。那么就需要总结下什么时候该使用StyleSpan,什么时候该使用多个TextView:
- 如果显示的是多个不同类别的信息,就应该使用多个TextView,这样也方便控制和改变各自的信息,例子就是默认LockScreen上面的日期和充电信息,因为它们所承载不同的信息,所以应该使用多个TextView来分别呈现。
- 如果显示的是同一类信息,或者同一个信息,那么应该使用StyleSpan。比如,短信息中,要把联系人的相关信息突出显示;或是想要Highlight某些信息等。
- 如果要实现Rich text,没办法,只能使用Style span。
- 如果要实现某些特效,也可以考虑使用StyleSpan。设置不同的字体风格只是Style span的初级应用,如果深入研究,可以发现很多奇妙的功效。