老規矩,先上張圖
o,這篇好像是分析篇,沒有效果圖。不管了,位置占着,老規矩不能壞,下面開始正文。
這篇博客會講得比較雜:
- TextView里各部分的大小該怎么測量?
- 如何計算每行文字的長度?
- 設置android:maxLines="1"和android:singleLine="true"有什么區別?
- 為什么設置android:maxLines="1"時TextView的跑馬燈效果就不能正常工作?
TextView里各部分的大小該怎么測量?
雖然我們平時只用TextView顯示純文本數據,但其實TextView支持設置Background,四周的drawable小圖標,以及Span數據比如文本或圖片。在Android里不管是什么控件都是占據一個矩形的空間,那么在一個TextView里該如何計算各個矩形的大小呢?下面是TextView提供的各個獲取長度寬度的接口:
getWidth(), getHeight()
getPaddingLeft/Right/Top/Bottom()
getCompoundPaddingLeft/Right/Top/Bottom()
getExtendedPaddingBottom/Top()
getTotalPaddingLeft/Right/Top/Bottom()
先看一下布局代碼:

@drawable/icon_person_n

下面是效果圖,做了一些標注:

下面來看一下上述獲取長度的接口的數據,你可以使用打日志方式,我是比較習慣用調試方式:

所以,總結一下:
getWidth(), getHeight():對應你代碼里的layout_width和layout_height。
getPaddiingLeft/Right/Top/Bottom():對應代碼里的Padding。
以上兩個比較容易理解,畢竟經常打交道。
getCompoundPaddingLeft/Top/Right/Bottom(): 翻譯成中文就是獲取混合的Padding, 既然是混合的,那么它的值也就是padding + 圖片的大小 + drawablePadding的值。說得通俗點就是,它是獲取文字區域到TextView邊界之間的間隔。附上源碼:

getExtendedPaddingTop():這個是當有部分文字沒有顯示出來時,也就是設置了maxLine時,它的值就等於首行文字到TextView頂端的距離。同理,getExtendedPaddingBottom()就是最后一行文字到TextVeiw底部距離。其他情況下,他的值等於getCompoundPaddingTop/Bottom()的值。這個源碼不多,但也不怎么好講解,就貼兩張圖對比下,就明白了。


getTotalPaddingLeft/Right/Top/Bottom():翻譯下就是獲取總的Padding值,看了下源碼,左右的值直接就是等於compoundPadding的值,上下的值等於ExtendedPadding的值再加上offset的值(跟Gravity的垂直方向的布局有關。說得通俗點就是,不管有沒有maxLines,上下的值都分別等於首行到TextView頂端和末行到TextView底部的值。
這些接口除了前面兩個比較常用外,其他基本很少用吧,我也是因為在看TextView的跑馬燈部分的源碼才接觸到,然后為了弄明白才記錄下來的。至於后面那些接口的應用場景,getCompoundPadding()這個的應用場景倒是很明確,可以用來判斷相應的drawable是否發生點擊事件之類的需求。至於extendedPadding和totalPadding這兩個的應用場景,我想了想,覺得應該是涉及需要計算顯示出來后的文字高度的相關需求時會用到吧。有對這些接口很熟悉的童鞋可以分享出來哈,一起學習學習。
最后用一張圖總結一下,我把TextView分成內容區域,內容區域和TextView邊界之間的間隔就是padding的值,內容區域包括drawable區域和文字區域,drawable區域和文字區域之間的間隔就是drawablePadding的值,文字區域和TextView之間的間隔就是CompoundPadding的值。

如何計算每行文字的長度?
Q:每行文字的長度不就等於TextView的寬度嗎?直接getWidth()不就好了?
A:再看一下上面那部分內容你就清楚了,只有當TextView寬度設置為wrap_content,且沒有背景圖或drawable時,文字的長度才等於getWidth();當文字很少,沒有填充滿時,或是溢出時,文字的長度都得另外計算。
Q:每行文字的長度不一樣長嗎?
A:因為TextView有自己的換行策略,如下圖所示,顯然每行的文字長度不一樣長。

Q:文字的長度是指哪段長度?
A:看需求吧,我覺得通常情況下都是只需要計算顯示在屏幕上的可見區域的每行文字的長度即可。還有那么一種需求,當設置了溢出內容用...表示時,那么其實每行文字的實際長度就不止可見區域那么長了。
那么該如何計算文字的長度呢?單單根據上一部分里的各種Padding值肯定不夠,根據各種Padding頂多計算出文字區域的寬度,但實際上每一行文字並不會那么剛剛好占滿文字區域的寬度,那么就還得借助其他來進行計算。
方法1:TextView.getPaint().measureText(String text)

但這種方法只是測試傳入的text在該TextView的配置下的總長度,並不是計算每一行的長度。
方法2:TextView.getLayout().getLineWidth(int line)

TextView對應的是圖14,正好,利用方法1驗證一下,這個方法計算得到的是不是每行文字的長度。

完全正確,所以說這個方法確實計算得到的是每一行文字的實際長度,注意這里是實際長度,也就是說當設置singleLine屬性時,用這個方法測量得到的是一整行文字的長度,包括溢出部分。
設置android:maxLines="1"和android:singleLine="true"有什么區別?
官方是推薦說不要再使用singleLine,用maxLines="1"代替。但其實這兩個的效果是不一樣的,官方api接口里有說明,都是英文我就不貼圖了,大概翻譯下:
maxLines:限制TextView的最高高度,大概就是指通過限制行數來限制最高高度。
singleLine: 強制設置TextView的文字不換行。
區別就是:maxLines還是會默認自動進行換行策略,假如一段文字自動換行后有5行,maxLines設置為1,那么就只顯示第一行的內容,其他行不顯示。
但是,如果是設置了singleLine, 那么這段可以有5行的文字將會被強制放在1行里,然后看最多能顯示多少字符,剩下的不顯示。
這樣的區別就是導致了很多人在使用TextVeiw的跑馬燈效果時不能正常工作的狀態,所以下面單獨列出個問題來講。
為什么設置android:maxLines="1"時TextView的跑馬燈效果就不能正常工作?
明白了maxLines="1"和singleLine的區別后,只要再明白跑馬燈的原理,就很容易理解為什么設置成maxLines="1"時跑馬燈不工作了。我在上一篇博客里寫過跑馬燈啟動的條件,具體的分析可以去上一篇看,這里大概說下。
跑馬燈要啟動要同時滿足四個條件,其中有一個條件就是這一行的文字長度要大於文字區域的寬度,文字區域的寬度就是TextView的getWidth()扣去ComPoundpaddingLeft再扣去CompoundPaddingRight剩下的長度。
如果是maxLines="1"的話,那么就像上一問中分析的那樣,所有的文字其實已經被自動換行了,只顯示第一行,而換行是什么,就是為了讓每行文字的長度超過文字區域的寬度才進行的換行,也就是說,如果一段文字經過TextView的換行后,那么每行的文字長度都不會超過文字區域的長度。這樣一來,自然就不滿足跑馬燈的啟動條件之一了,跑馬燈也就不能正常工作了。
singleLine的話,則是不會對一段文字進行換行處理,這樣一來,自然就超過了文字區域的長度,所以如果要設置跑馬燈效果的話,只能用singleLine不能用maxLines="1"。

最近剛開通了公眾號,想激勵自己堅持寫作下去,初期主要分享原創的Android或Android-Tv方面的小知識,感興趣的可以點一波關注,謝謝支持~~
