紧跟上一篇Android 端 (图文混排)富文本编辑器的开发(一)
这一篇会对 Android 中的Span进行简单的介绍,并且会结合实际的需求对 span 进行应用,完成编辑器中粗体、斜体、下划线、中划线 功能
1.Span介绍
1.1 应用Span
在使用Span时,经常会接触到SpannableString 和 SpannableStringBuilder 两个类。
这两个类的区别在于文本是否可变,类似于String与StringBuilder之间的关系,SpannableStringBuilder可以修改文本内容。
SpannableString与SpannableStringBuilder 都实现了 Spannable接口,Spannable接口继承了Spanned 接口。
先看 Spanned接口重要的方法,方法上 加了中文注释:
/**
* Return an array of the markup objects attached to the specified
* slice of this CharSequence and whose type is the specified type
* or a subclass of it. Specify Object.class for the type if you
* want all the objects regardless of type.
*/
//---获取 从 start 到 end 位置上所有的指定 class 类型的 Span数组
public T[] getSpans(int start, int end, Class type);
/**
* Return the beginning of the range of text to which the specified
* markup object is attached, or -1 if the object is not attached.
*/
//获取 一个 span 的起始位置
public int getSpanStart(Object tag);
/**
* Return the end of the range of text to which the specified
* markup object is attached, or -1 if the object is not attached.
*/
//获取一个span 的结束位置
public int getSpanEnd(Object tag);
/**
* Return the first offset greater than start
where a markup
* object of class type
begins or ends, or limit
* if there are no starts or ends greater than start
but less
* than limit
. Specify null
or Object.class for
* the type if you want every transition regardless of type.
*/
// 在指定的文本范围内,返回下一个 指定 class 类型的 span开始
public int nextSpanTransition(int start, int limit, Class type);
接下来看 Spannable 接口方法:
/**
* Attach the specified markup object to the range start…end
* of the text, or move the object to that range if it was already
* attached elsewhere. See {@link Spanned} for an explanation of
* what the flags mean. The object can be one that has meaning only
* within your application, or it can be one that the text system will
* use to affect text display or behavior. Some noteworthy ones are
* the subclasses of {@link android.text.style.CharacterStyle} and
* {@link android.text.style.ParagraphStyle}, and
* {@link android.text.TextWatcher} and
* {@link android.text.SpanWatcher}.
*/
//----设置 span 这里的 what 指的是 span 对象
// 从 start 到 end 位置 设置 span 样式
// flags 为 Spanned中的变量,接下来会分析到
public void setSpan(Object what, int start, int end, int flags);
/**
* Remove the specified object from the range of text to which it
* was attached, if any. It is OK to remove an object that was never
* attached in the first place.
*/
// 在spannable 中移出指定的 span
public void removeSpan(Object what);
上面四种 flags
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括);
Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,后面不包括);
Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,后面包括);
Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)。
1.2 Span 的分类
1.影响字符级别
这一类型的span 作用范围是 字符级别,通过设置 TextPaint来影响字符的外观,大小等。
1.字符外观
这种类型修改字符的外形但是不影响字符的测量,会触发文本重新绘制但是不触发重新布局。
常见的
BackgroundColorSpan
var str1 = SpannableString("测试BackgroundColorSpan使用")
str1.setSpan(BackgroundColorSpan(Color.GREEN), 2, str1.length - 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_background.setText(str1)
ForegroundColorSpan
var str2 = SpannableString("测试ForegroundColorSpan使用")
str2.setSpan(ForegroundColorSpan(Color.RED), 2, str2.length - 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_forground.setText(str2)
UnderlineSpan
var str3 = SpannableString("测试UnderlineSpan使用")
str3.setSpan(UnderlineSpan(), 2, str3.length - 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_underline.setText(str3)
StrikethrougnSpan
var str4 = SpannableString("测试StrikethrougnSpan使用")
str4.setSpan(StrikethroughSpan(), 2, str4.length - 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_strikethrough.setText(str4)
2.字符大小布局
这种类型Span会更改文本的大小和布局,会触发文本的重新测量绘制
常见的
StyleSpan
var str5 = SpannableString("测试StyleSpan使用")
str5.setSpan(StyleSpan(Typeface.BOLD),2,7,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
str5.setSpan(StyleSpan(Typeface.ITALIC),7,str5.length-2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_style.setText(str5)
RelativeSizeSpan
var str6 = SpannableString("测试 RelativeSizeSpan 使用")
str6.setSpan(RelativeSizeSpan(1.5f),2,str6.length-2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tv_relactive.setText(str6)