2024年安卓最全Android Span详解(1),面试必问知识点有哪些

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 基于Span是否改变text的外形还是改变text的尺寸或布局

  • 基于Span的作用范围是字符级别还是或段落级别

Span的实现原理是,Android framework定义了几个接口和抽象类,这些接口和抽象类有允许Span访问TextPaint或Canvas对象的方法,它们会在测量和渲染时被检查,达到改变文本样式的效果。

影响text外观的Span

这些Span可以影响text外观:文本或背景颜色、下划线、删除线等等,如下UML类图所示,类名所见即所得。

这些Span会触发文本重新绘制,而不会触发重新布局。这些 span 实现了 UpdateAppearance 且继承自 CharacterStyle。CharacterStyle的子类通过提供更新 TextPaint 的访问方法,定义了怎样绘制文本。

影响text尺寸或布局的Span

这些Span可以影响text的尺寸和布局,如文本绝对尺寸、相对尺寸、插入图片、上标、下标、字体、字体风格等,如下UML类图所示,类名所见即所得。这些Span都继承自MetricAffectingSpan。

影响文本字体大小的Span可能会使得text字符宽高变化,甚至多出来一行,其实现是通过监听,触发重新测量、进而重新计算布局,进而重新绘制。这写Span继承自MetricAffectingSpan类,这个抽象类通过提供对 TextPaint的访问,来影响文本测量,而 MetricAffectingSpan 继承自CharacterSpan,其子类在字符级别影响文本的外形。

字符级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);

}

}

Span使用最佳实践


基于使用场景,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) {

SpannableString spannable = new SpannableString(“Text is spantastic”);

// setting the span to the bulletSpan field

最后

题外话,我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

Android开发8年,阿里、百度一面惨被吊打!我是否应该转行了?

【Android进阶学习视频】、【全套Android面试秘籍】

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

[外链图片转存中…(img-OocDNKfD-1715728810290)]

【Android进阶学习视频】、【全套Android面试秘籍】

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值