Hi,你们好,我是承香墨影!算法
前两天聊了一下 Autosizing 的使用,反映还不错。毕竟是这种能解决实际问题的新 Api,确实在须要的时候,用起来会很顺手。c#
简单回顾一下,Autosizing 是在 Support v26 中新支持的功能,能够根据文本的内容和 TextView 的大小,自动适应齐内部文本的字体大小,来达到彻底显示的效果。而这个功能,最低能兼容到 Api Level 14,能够说是一个诚意满满的新 Api。数组
还不了解 Autosizing 的朋友,能够看看以前的文章《文字太多?控件过小?试试 TextView 的新特性 Autosizeing 吧!》,里面有使用它的详细介绍。微信
我想,在没有 Autosizing 的时候,应该已经有人以这样的思路在实现功能了。那么,今天就来从源码的角度分析一下,Autosizing 的原理如何,看看它是如何工做的。ide
分析源码也是讲究方式方法的,我主推的一个思路,就是带着问题看源码。工具
不少大型项目,其实自己都是很复杂的,而且涵盖的功能点也很是的多,若是想要一次就把它完整的阅读屡清楚,仍是很吃力的。布局
因此我建议在阅读开源项目以前,你先阅读文档,尝试使用一下它,看看它能作什么,再本身思考一下,若是你是做者,你会如何去实现这些功能的,最后带着这些问题去阅读源码,以问题为出发点,看看那些大牛写的优秀的开源库,到底有什么值得咱们借鉴的地方。字体
总归一句话:阅读源码是为了更好的编写源码!设计
当我看到到 Autosizing 这个新特性的时候,我有一些好奇的地方在于:3d
大概就是这些问题吧,接下来咱们看看 Autosizing 是如何实现的。
对于 Android 8.0 中和 Support v26 中,具体对于 Autosizing 的实现,有什么区别这一点,大体阅读一下两边的源码,你会发现大体上没区别。
它们之间,和 Autosizing 相关的源码所在的源码文件也不同:
随手比对一下它们的 setAutoSizeTextTypeWithDefaults()
方法,这个方法用来标记是否对 TextView 开启 Autosizing。
左边是 Android 8.0 的 TextView ,右边是 AppCompatTextViewAutoSizeHelper。
能够看到,整个代码的结构都是一致的,只是部分引用的类不同而已,可是表达的意思是一致的。
之因此说它们以前大体是同样的,是由于有一些 Api 是 private
的或者被标记为 @hide
了,这样,在外部是没法访问到的。对此 AppCompatTextViewAutoSizeHelper 的作法是用反射的形式去调用它。
例如,实际去修改 TextView 尺寸的方法 autoSizeText()
,看下面它们的区别。
左边是 Android 8.0 的 TextView ,右边是 AppCompatTextViewAutoSizeHelper。
两边都须要获取 mHorizontallyScrolling 的值,TextView 内部固然能够直接调用了,而 AppCompatTextViewAutoSizeHelper 的作法是,使用 invokeAndReturnWithDefault()
方法,经过反射区获取这个值。
因此,咱们能够得出结论,两边的实现思路,大致上是没有区别的,只是有一些小细节,会不同,可是咱们不须要太在乎这些。
既然,两边的区别不大,以后咱们就以 Support v26 中,关于 Autosizing 的源码实现来进行分析。
首先这个时机让我本身来设计,也很是好理解。
本质上 Autosizing 就是为了让 TextView 中的文本,能彻底显示,在这个过程当中会去调整 文本 的字体大小。
那这样,触发它的时机,其实就很容易猜到了:
Support v26 中,之因此能保证兼容,本质上它会自动将 TextView 这样的控件,替换成 AppCompatTextView 来达到兼容的效果,这个过程当中,开发者只须要使用 AppCompatActivity 就能够了,其它的不须要开发者来参与。这样,其实咱们只须要关注 AppCompatTextView 中的实现逻辑就行了。
前面提到,操做 Autosizing 的具体源码,在 AppCompatTextViewAutoSizeHelper 中。 而 AppCompatTextView 并不直接操做它。
首先 AppCompatTextVIew 会持有 AppCompatTextHelper 这个帮助类,而这个帮助类,又去持有 AppCompatTextViewAutoSizeHelper,最终全部的逻辑都传递到 AppCompatTextViewAutoSizeHelper 中去处理。
因此操做的流程大概是这样的:
而 Autosizing 真实去测量并修改字体大小的逻辑,都在 autoSizeText()
方法中,咱们只须要关心它在什么时候被调用,就能知道具体触发 Autosizing 的时机了。
第一个触发点,它会在 AppCompatTextView 的 onTextChanged()
方法中,直接调用 autoSizeText()
方法。
第二个触发点,会监听 AppCompatTextView 的 onLayout()
方法,在其中调用 AppCompatTextHelper 的 onLayout()
方法。
好了,两个时机都找到了,也验证了咱们以前的猜测。
前面提到 Autosizing 实际上去修改 TextView 字体的方法,在 AppCompatTextViewAutoSizeHelper 的 autoSizeText()
方法中,这里咱们先来看看这个方法的实现。
这一段逻辑,就是 Autosizing 中,很重要的一个逻辑。先来看看它大致上的流程。
isAutoSizeEnabled()
方法,判断当前是否开启 Autosizing 。mNeedsAutoSizeText
是否为 true,此处判断是主要是看是否存在可变更的尺寸。TEMP_RECTF
中。findLargestTextSizeWhichFits()
获取到一个合适当前文本长度的最大尺寸值。setTextSizeInternal()
将其设置回去。大致步骤就是这样,接下来咱们从细节出发看看它的具体实现。
首先是 isAutoSizeEnable()
方法,它去判断当前是否开启了 Autosizing,其实就是判断 mAutoSizeTextType 属性是否为 none。
而 mNeedsAutoSizeText
这个判断,本质上实际上是为了判断 mAutoSizeTextSizesInPx
这个存放尺寸的数组里,是否有值,这个尺寸数组,在后面的 findLargestTextSizeWhichFits()
方法中会用到。
mAutoSizeTextSizesInPx
其实就是一个存放当前 TextView 预估能使用的尺寸数组,是被提早计算出来的,它会在对 Autosizing 受影响的相关的属性作出修改的时候,从新计算。例如:粒度(Granularity)、预设尺寸(PresetSizes)等变更,都会触发从新计算 mAutoSizeTextSizesInPx
的值。
TEMP_RECTF
就没有什么好说的了,无非就是从 TextView 的宽高和 Padding 等属性,计算出一个能用于显示 文本 区域大小。接下来就会去调用 findLargestTextSizeWhichFits()
方法,找到一个当前 文本 内容,最合适的字体大小。
这里逻辑也很清晰,就是使用一个循环,经过 suggestedSizeFitsInSpace()
方法判断取出来的尺寸是否合适。这里为了提升效率,使用了二分算法,去避免所有遍历 mAutoSizeTextSizesInPx 数组,从而提升效率。
接下来就是 suggestedSizeFitsInSpace()
方法,它会根据 TextView 的内容区域和 文本,判断当前给定的尺寸,是否能放的下这些内容。
这里首先使用了一个 TextPaint 对象 mTempTextPaint 来存放 TextView 的一些参数,而后根据 mTempTextPaint 去建立一个使用 StaticLayout 对象,来尝试对文本进行布局。
StaticLayout 是一个为不可编辑的文本布局的类,这意味着一旦布局完成,文本内容就不能够改变。
最终,就能肯定,传递进行的字体大小,是否能彻底显示在这个区域内。
通过这一通计算,findLargestTextSizeWhichFits()
方法,最终将计算出来的一个合适的字体尺寸,返回回去,再经过 setTextSizeInternal()
设置到 TextView,来达到修改字体大小的目的。
如今来看,Autosizing 计算某段文本,在一个 固定的 TextView 中,将展现的单行宽度和行数这个功能,这些算是 Autosizing 中,比较有借鉴意义的功能了。
其它的我暂时没有想到,你以为还有什么能够借鉴的点呢?在留言中告诉我。
今天在承香墨影公众号的后台,回复『成长』,我会送你一些特别的内容。
我另外还维护了一个技术交流的微信群,有兴趣能够在公众号后台回复:"加群"
推荐阅读: