TextView 应该是Android 中使用频率最高的控件之一,其重要性不言而喻
在平时的开发中,对于TextView的使用方法无非就是在layout 中设置一下布局、样式、颜色等,要是需要进行改变,丢到handler里
复杂一点的可以引入字体文件,通过资源进行设置
但是如果要在一定的范围内改变字体的样式怎么解决?拆分String,用两个TextView?
如果要让TextView实现类似于超链接的跳转功能怎么解决?添加监听事件,配合Intent使用?
再牛逼点,如果我要你TextView 显示图片,咋整? 啥?强转会报错?
现在,就正式请出今天的主角,SpannableString和SpannableStringBuilder
先说说二者的区别, SpannableString的长度固定,而SpannableStringBuilder的长度是可以改变的
本文着重就以SpannableString为例,至于SpannableStringBuilder的使用,我相信每个开发人员都懂得触类旁通,就不多介绍了
首先,我们先看下SpannableString的源码
public class SpannableString
extends SpannableStringInternal
implements CharSequence, GetChars, Spannable
{
public SpannableString(CharSequence source) {
super(source, 0, source.length());
}
private SpannableString(CharSequence source, int start, int end) {
super(source, start, end);
}
public static SpannableString valueOf(CharSequence source) {
if (source instanceof SpannableString) {
return (SpannableString) source;
} else {
return new SpannableString(source);
}
}
public void setSpan(Object what, int start, int end, int flags) {
super.setSpan(what, start, end, flags);
}
public void removeSpan(Object what) {
super.removeSpan(what);
}
public final CharSequence subSequence(int start, int end) {
return new SpannableString(this, start, end);
}
}
继承了抽象类SpannableStringInternal,和三个接口CharSequence, GetChars, Spannable
构造函数
public SpannableString(CharSequence source) {
super(source, 0, source.length());
}
参数就不多解释了
再看看setSpan方法
public void setSpan(Object what, int start, int end, int flags) {
super.setSpan(what, start, end, flags);
}
传入的参数有四个,这里解释下
/**
* what 字体设置的格式
* start 所设置字体的起始下标
* end 所设置结束下标
* flags 下标取值标志,取值:
* Spanned.SPAN_INCLUSIVE_EXCLUSIVE 从起始下标到结束下标,包括起始下标
* Spanned.SPAN_INCLUSIVE_INCLUSIVE 从起始下标到结束下标,同时包括起始下标和结束下标
* Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 从起始下标到终了下标,但都不包括起始下标和结束下标
* Spanned.SPAN_EXCLUSIVE_INCLUSIVE 从起始下标到结束下标,包括结束下标
**/
有点拗口,但就是高中数学里交集并集的关系
如果实在不行,就看看我这边的栗子
package com.example.textviewdemo;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.widget.EditText;
public class MainActivity extends Activity {
/**
* what 字体设置的格式
* start 所设置字体的起始下标
* end 所设置结束下标
* flags 下标取值标志,取值:
* Spanned.SPAN_INCLUSIVE_EXCLUSIVE 从起始下标到结束下标,包括起始下标
* Spanned.SPAN_INCLUSIVE_INCLUSIVE 从起始下标到结束下标,同时包括起始下标和结束下标
* Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 从起始下标到终了下标,但都不包括起始下标和结束下标
* Spanned.SPAN_EXCLUSIVE_INCLUSIVE 从起始下标到结束下标,包括结束下标
**/
private EditText mEt1,mEt2,mEt3,mEt4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
mEt1 = (EditText)findViewById(R.id.et1);
mEt2 = (EditText)findViewById(R.id.et2);
mEt3 = (EditText)findViewById(R.id.et3);
mEt4 = (EditText)findViewById(R.id.et4);
//构造SpannableString
SpannableString spanString = new SpannableString("天王盖地虎宝塔镇河妖");
SpannableString spanString2 = new SpannableString("天王盖地虎宝塔镇河妖");
SpannableString spanString3 = new SpannableString("天王盖地虎宝塔镇河妖");
SpannableString spanString4 = new SpannableString("天王盖地虎宝塔镇河妖");
//用于改变字体颜色的ForegroundColorSpan
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
//设定范围,传入不同的flag
spanString.setSpan(span, 1, 3, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
spanString2.setSpan(span, 1, 3, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
spanString3.setSpan(span, 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString4.setSpan(span, 1, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
//在不同的EditText中显示
mEt1.setText(spanString);
mEt2.setText(spanString2);
mEt3.setText(spanString3);
mEt4.setText(spanString4);
}
}
然后分别在前后插入字符,效果如下
其他设置
上面的那颗栗子用到了设置颜色的类,但仅仅只能设置颜色,如何装逼?
//改变字体背景色的span
BackgroundColorSpan colorSpan = new BackgroundColorSpan(Color.BLUE);
//改变字体大小的span,传入一个int 值
AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(16);
//设置文字相对大小的span,传入一个float,以原有字体的大小为基础设置倍数
RelativeSizeSpan rSizeSpan = new RelativeSizeSpan(1.5f);
//一些文本标志,以下划线为例
UnderlineSpan lineSpan = new UnderlineSpan();
当然,除了这些,还有删除线(StrikethroughSpan) 文本样式(StyleSpan)等其他样式
上例的运行结果如图
介绍完样式使用,接下来隆重介绍两种更有逼格的用法
首先,试试在字段中加入一个超链接吧,代码如下
//超链接文本
SpannableString spanString6 = new SpannableString("天王盖地虎宝塔镇河妖");
URLSpan urlSpan = new URLSpan("http://www.baidu.com");
spanString6.setSpan(urlSpan,1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv1.setMovementMethod(LinkMovementMethod.getInstance());
mTv1.setHighlightColor(Color.BLUE);
mTv1.setText(spanString6);
运行效果:
点击后自动跳转页面,不用给TextView 添加监听事件,是不是很酷
你以为就这些功能?
前些天在倒腾聊天页面,要实现图片、文字的发送,文字用TextView 加到Item中即可,但是要在ListView 中显示图片就有些麻烦了。
我最开始的思路是在一个布局里同时存放ImageView 和TextView ,然后根据类型进行判断,将其设置为VISILIABLE 和GONE
但是这种方法使得适配器的代码过于冗长,而且findViewById 的开销有比较大,这才想到了也许可以用SpannableString来结局这个问题
废话少说,上代码
SpannableString spanString7 = new SpannableString("天王盖地虎宝塔镇河妖");
Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);
//设置图片大小
drawable.setBounds(0, 0, 66, 66);
ImageSpan imageSpan = new ImageSpan(drawable);
spanString7.setSpan(imageSpan, 0, spanString7.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTv2.setText(spanString7);
上述所有的例子显示都如下图所示,顺便提一下聊天界面中Item 的思路,将接收到的图片保存至本地后将url保存到List中,首先判断数据的类型,如果是为图片,则直接将TextView 设置成该地址所对应的图片,将其显示出来,怎么样,是不是又方便又有逼格?