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 中,比較有借鑑意義的功能了。
其它的我暫時沒有想到,你以爲還有什麼能夠借鑑的點呢?在留言中告訴我。
今天在承香墨影公衆號的後臺,回覆『成長』,我會送你一些特別的內容。
我另外還維護了一個技術交流的微信羣,有興趣能夠在公衆號後臺回覆:"加羣"
推薦閱讀: