字符级Span
抽象类CharacterStyle对文本产生的影响在字符级别,更新元素,如背景颜色、样式或大小,上面的影响text外观、影响text尺寸或布局的Span都是字符级的Span。
CharacterStyle主要就是一个抽象方法updateDrawState,影响绘制属性,总结下来就是,一支画笔走天下,什么效果都能渲染。
MetricAffectingSpan主要就是一个抽象方法updateMeasureState,影响测量,进而重新布局。
段落级Span
段落级别Span都实现了接口ParagraphStyle(空接口),这些Span可以更改整个文本块的对齐方式或者边距。继承自ParagraphStyle的Span必须作用于text整体,从第一个字符附加到单个段落的最后一个字符,否则Span不会被显示。
在 Android 中,段落是基于换行符 (\n) 定义的。
Framework中段落级的Span,如下UML类图所示,类名所见即所得。可以看到很多接口没有实现,系统是预留了很多能力的,方便自定义。
自定义Span
系统提供的Span样式虽多,但是未必有一款合你心意,自定义Span总是在所难免。在实现你自己的Span时,需要确定你的Span是会影响字符级别还是影响段落级别的文本,以及它是影响文本的布局还是影响文本的外观,据此选择需要扩展的基类和实现的接口。相应选择如下:
举个例子,你需要Span样式可以改变文本的大小和颜色。你可以扩展RelativeSizeSpan,由于 RelativeSizeSpan已经提供了updateDrawState和updateMeasureState回调,我们可以复写绘制状态回调并设置 TextPaint 的颜色。这只是一个自定义Span的例子而已,同样的效果你可以通过组合使用RelativeSizeSpan和ForegroundColorSpan来达成。
public class RelativeSizeColorSpan extends RelativeSizeSpan {
private int color;
public RelativeSizeColorSpan(float spanSize, int spanColor) {
super(spanSize);
color = spanColor;
}
@Override
public void updateDrawState(TextPaint textPaint) {
super.updateDrawState(textPaint);
textPaint.setColor(color);
}
}
基于使用场景,TextView#setText()方法有几种优化内存的方式。原理是,setText方法会copy一份text实例,在某些场景可以规避创建copy text实例。
text不变增加或移除Span
TextView#setText()因处理不同的Span有多个重载,例如,设置一个Spannable text:
textView.setText(spannableObject);
当调用setText()方法,TextView会copy Spannable作为SpannableString,并在内存中以CharSequence形态保存。这意味着text和Span是不可变的,当需要更新text和Span时,需要创建新的Spannable,并且调用setText()。
如果Span是可变的,使用setText(CharSequence text, TextView.BufferType type)更佳, 如下:
textView.setText(spannable, BufferType.SPANNABLE);
Spannable spannableText = (Spannable) textView.getText();
spannableText.setSpan(
new ForegroundColorSpan(color),
8, spannableText.getLength(),
SPAN_INCLUSIVE_INCLUSIVE);
上例中,由于BufferType.SPANNABLE参数,setText方法创建了SpannableString(可变markup,不可变文本),再次更新Span时,可以获取TextView中的Spannable引用,而非再次创建新的Spannable实例,优化内存使用。
需要注意的是,此时需要主动调用invalidate() 或者requestLayout(),根据更新的Span是影响外观的,还是影响尺寸和布局的而定。
TextView多次设置text
一些场景,比如RecyclerView.ViewHolder,存在TextView复用,导致多次设置text。
通常不使用BufferType参数的情况下,每次设置文本,TextView都会copy一份实例,以CharSequence的形态存在内存中。也就是,每次设置新的文本,TextView都会创建新的实例。
通过实现自己的Spannable#Factory并重写newSpannable()可以控制这个过程,并避免多余实例的创建。范例如下:
Spannable.Factory spannableFactory = new Spannable.Factory(){
@Override
public Spannable newSpannable(CharSequence source) {
return (Spannable) source;
}
};
需要注意的是,必须使用textView.setText(spannableObject, BufferType.SPANNABLE)这种方式设置文本,否则就会抛出ClassCastException。
需要告诉TextView使用自定义的Spannable#Factory,如下:
textView.setSpannableFactory(spannableFactory);
在获得TextView引用之后需要立刻设置,如果在使用RecyclerView,应该在view第一次被inflate出来之后立刻设置Factory,避免绑定数据时TextView#setText()出现多余的实例创建。
改变Span属性
如果需要改变一个可变Span的内部属性,比如改变BulletSpan的颜色,避免多次重头调用setText()方法,最佳实现方式是,保存Span的引用,再需要更新Span属性时,通过引用改变属性,然后调用invalidate() 或者 requestLayout()方法。
BulletSpan颜色改变的范例如下:
public class MainActivity extends AppCompatActivity {
private BulletPointSpan bulletSpan = new BulletPointSpan(Color.RED);
@Override
protected void onCreate(Bundle savedInstanceState) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后
下面是辛苦给大家整理的学习路线,有需要的可以点击这里免费获取
06532)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-mR3oSMgK-1711291706532)]
最后
下面是辛苦给大家整理的学习路线,有需要的可以点击这里免费获取
[外链图片转存中…(img-ZDpfofGW-1711291706533)]