淺扒Android動態設定字型大小
說點廢話
Android開發中,TextView類的控制元件應該說是很常用了。一般來說我們是通過android:textSize="20sp"
來設定字型大小,但是很多時候也需要動態設定字型大小,呼叫也很簡單:
textView.setTextSize(textSize);
為了適配各種各樣的型號,我們一般會將字型大小定義到dimens.xml
之中:
<dimen name="text_size">16sp</dimen>
然後在java
程式碼中設定定義好的字型大小:
float dimen = getResources().getDimension(R.dimen.text_size);
textView.setTextSize(dimen);
滿心歡喜的執行一下,看一效果,結果發現字型奇大無比!!!遠非16sp!!!難道不應該通過getDimension()
取值嗎?通過logcat
我發現,在Nexus 6p
並且<dimen name="text_size">16sp</dimen>
下,在通過getDimension(R.dimen.text_size)
得到返回值是56.0
!
實際上,在java
程式碼中取在dimens.xml
中定義的值一共有三種:
- getDimension()
- getDimensionPixelOffset()
- getDimensionPixelSize()
看到這三個函式的名稱時,還是會有點不知所云。本著“不求甚解,遍歷式開發”的原則,我把這三種方式都試了一遍,結果發現字型大小沒一個是對的,這就詭異了。難道這裡有平行宇宙?至此,我只能翻出我的英漢大詞典,讓我們去探尋一下docs
吧。
getDimension()
/**
* Retrieve a dimensional for a particular resource ID. Unit
* conversions are based on the current {@link DisplayMetrics} associated
* with the resources.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* @return Resource dimension value multiplied by the appropriate
* metric.
*/
public float getDimension(@DimenRes int id) throws NotFoundException {
}
通過註釋我們不難發現,getDimension()
是根據指定id獲取一個基於當前DisplayMetrics
的值。這個值究竟是什麼也沒有說,只知道是float
,並且單位轉換是基於當前資源的,但肯定不是畫素,如果是畫素應該是int。
getDimensionPixelSize
/**
* Retrieve a dimensional for a particular resource ID for use
* as a size in raw pixels. This is the same as
* {@link #getDimension}, except the returned value is converted to
* integer pixels for use as a size. A size conversion involves
* rounding the base value, and ensuring that a non-zero base value
* is at least one pixel in size.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* @return Resource dimension value multiplied by the appropriate
* metric and truncated to integer pixels.
*/
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
}
getDimensionPixelSize()
的功能與getDimension()
類似,不同的是將結果轉換為int,並且小數部分四捨五入,這個結果將作為尺寸。getDimensionPixelSize()
進行了尺寸轉換,這個轉換實際是上四捨五入的結果,並且保證返回值是一個至少是1畫素的非零數值。
getDimensionPixelOffset()
/**
* Retrieve a dimensional for a particular resource ID for use
* as an offset in raw pixels. This is the same as
* {@link #getDimension}, except the returned value is converted to
* integer pixels for you. An offset conversion involves simply
* truncating the base value to an integer.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* @return Resource dimension value multiplied by the appropriate
* metric and truncated to integer pixels.
*/
public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
}
getDimensionPixelOffset()
與getDimension()
功能類似,不同的是將結果轉換為int,這個結果將用作原始畫素的偏移量。偏移轉換(offset conversion,函式命名中的offset是這個意思)的作用之一是將基礎值簡單地截短為整數,注意直接截斷小數位,即取整(其實就是把float強制轉化為int,注意不是四捨五入)。
階段性總結
由此可見,這三個函式返回的都是絕對尺寸,而不是相對尺寸(dpsp等)。如果getDimension()返回結果是30.5f,那麼getDimensionPixelSize()返回結果就是31,getDimensionPixelOffset()返回結果就是30。
至此,應該說getDimensionPixelSize()
getDimension()
getDimensionPixelOffset()
我們已經大致有所瞭解了,但是如果想更深入瞭解一下,就需要深入原始碼以驗證上述解釋。
扒原始碼
深入原始碼,我們可以發現其實這三個函式的實現大同小異,以getDimension()
為例:
public float getDimension(@DimenRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimension(value.data, impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
} finally {
releaseTempTypedValue(value);
}
}
類TypedValue
是動態型別資料的容器,其主要用於盛放Resources
的值。上述程式碼第7行就是根據id獲取TypedValue
的值,getDimension()
、getDimensionPixelOffset()
和getDimensionPixelSize()
函式體唯一的不同就是第7行:
-
getDimension()
呼叫的是TypedValue
的complexToDimension()
方法 -
getDimensionPixelSize
呼叫的是TypedValue
的complexToDimensionPixelSize()
方法 -
getDimensionPixelOffset
呼叫的是TypedValue
的complexToDimensionPixelOffset()
方法
順藤摸瓜,我們繼續深入ypedValue
,檢視complexToDimension()
、complexToDimensionPixelSize()
和complexToDimensionPixelOffset()
函式的區別,會發現這三個函式體內容依舊大同小異,以complexToDimension()
為例:
public static float complexToDimension(int data, DisplayMetrics metrics) {
return applyDimension(
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
complexToFloat(data),
metrics);
}
complexToDimensionPixelOffset()
與complexToDimension()
不同的是將結果進行了強轉,實際上相當直接截斷小數部分;complexToDimensionPixelSize()
是將結果進行四捨五入,並取整。這裡的四捨五入實際上就是把結果加上0.5f然後進行強轉(有興趣瞭解原理的可以留言)。
applyDimension()
各位看官,原始碼已經看到了這裡,是否已感覺很無趣?但applyDimension()
的實現已經脫光了在等著你呢:
public static float applyDimension(int unit, float value,DisplayMetrics metrics) {
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
在上述程式碼中,我們發現在applyDimension()
中根據單位的不同,將float
乘上不同的係數。如dip/dp需乘上螢幕係數,sp則需乘上字號的縮放係數,pt、in、mm等也是根據相應的演算法進行換算(從COMPLEX_UNIT_PX直接返回float可以看出,該方法是將數值轉成畫素數)。
再次總結
通過上述探索,我們不難發現,在Adroid並沒有在java
程式碼中直接獲取dimens.xml
中定義的dp(dip)/sp的值的API,只有getDimension()
、getDimensionPixelOffset()
和getDimensionPixelSize()
這個三個方法來獲取絕對尺寸。但有時候我們確實需要動態獲取dimen.xml
中的值,併為TextView設定字型大小。而這種方法直接應用在textView.setTextSize(dimen);
都是有問題的。那我們將從TextView
入手,尋找一個正確的姿勢來設定字型大小。
setTextSize()
首先把程式碼端上來:
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
原來setTextSize(float)
呼叫了他的過載方法setTextSize(int,float)
,並且第一個引數傳的預設值是TypedValue.COMPLEX_UNIT_SP
,眼熟嗎,沒錯就是之前提到的。那麼,我們繼續看看一下setTextSize(int,float)
做了什麼:
public void setTextSize(int unit, float size) {
if (!isAutoSizeEnabled()) {
setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
}
}
很顯然是呼叫了setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
。看到這累不,不過看都看了就再看看唄,說不定比蒼老師好看:
private void setTextSizeInternal(int unit, float size, boolean shouldRequestLayout) {
Context c = getContext();
Resources r;
if (c == null) {
r = Resources.getSystem();
} else {
r = c.getResources();
}
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),shouldRequestLayout);
}
高能!!!TypedValue.applyDimension(unit, size, r.getDisplayMetrics())是不是很眼熟???還記得applyDimension()
是怎麼處理資料的嗎?
- 我們發現在
applyDimension()
中根據單位的不同,將float
乘上不同的係數。如dip/dp需乘上螢幕係數,sp則需乘上字號的縮放係數,pt、in、mm等也是根據相應的演算法進行換算(從COMPLEX_UNIT_PX直接返回float可以看出,該方法是將數值轉成畫素數)
綜上,setTextSize(float)
給傳的值的單位其實是SP,但通過getDimension()
取的值卻不是這樣的。為了證實預設單位是SP,各位看官可以直接傳個16,看看和16sp是不是一樣的。所以問題是不得到了解決?
結論
Android中並不提供直接從dimens.xml
獲取dp/sp數值的方法,通過getDimensionPixelSize()
getDimension()
getDimensionPixelOffset()
獲取的值是經過處理的。所以正確地動態設定TextView
字型大小的姿勢應該是:
int dimen = getResources().getDimensionPixelSize(R.dimen.text_size);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,dimen);
相關文章
- JavaScript動態設定文字字型大小JavaScript
- 直播軟體開發,通過js動態設定字型大小JS
- win10電腦字型大小怎麼設定_win10如何設定字型大小Win10
- Android開發之動態設定字型的樣式和粗細Android
- CSS 設定字型顏色和大小CSS
- IDEA-idea設定導航欄字型大小程式碼編輯區字型大小Idea
- Android中TabLayout修改字型大小AndroidTabLayout
- android 字型設定為中等粗細Android
- Pycharm-Pycharm設定左側導航欄字型大小和程式碼編輯區字型大小PyCharm
- 短視訊直播系統,Android狀態列設定顏色字型Android
- 點選大中小按鈕設定文章字型大小
- 系統文字大小怎麼設定_win10電腦系統字型大小在哪裡設定Win10
- win10 工作列選單字型大小怎麼調_win10選單字型大小怎麼設定Win10
- Android FixedTextView 字型大小自適應文字框AndroidTextView
- ubuntu怎麼把字型變大? ubuntu20.04設定字型大小的兩種技巧Ubuntu
- 短視訊商城系統,Android TextView自動調整字型大小AndroidTextView
- android短視訊開發,設定APP字型大小不隨著系統變化而改變AndroidAPP
- 用js實現動態改變根元素字型大小的方法JS
- Android平臺targetSdkVersion設定及動態許可權Android
- win10桌面圖示字型大小怎麼調_win10桌面圖示字型大小如何設定Win10
- 【UniApp】-uni-app-動態計算字型大小(蘋果計算器)APP蘋果
- Win10系統如何更改視窗元件字型大小 win10視窗字型設定Win10元件
- TextMeshPro - 字型設定
- sqldeveloper 字型設定SQLDeveloper
- css字型設定CSS
- Android佈局中動態新增ImageView並設定間隔AndroidView
- matplotlib預設字型設定
- Android NDK祕籍--淺析靜態庫和動態庫Android
- jquery動態設定selectjQuery
- AngularJS動態設定CSSAngularJSCSS
- 網頁字型大小font-size設定其實不管用 @tonsky.me網頁
- Android開發 - 掌握ConstraintLayout(十)按比例設定檢視大小AndroidAI
- JavaScript動態設定float浮動JavaScript
- 扒一扒React計算狀態的原理React
- Android 仿微信/支付寶 字型大小 調整控制元件Android控制元件
- Android View篇之調整字型大小滑桿的實現AndroidView
- Mac Jenkins 構建 Android App 時動態設定程式碼引數MacJenkinsAndroidAPP
- ReactNative字型大小不隨系統字型大小變化而變化React
- 直播帶貨系統原始碼利用TextView設定部分字型的顏色和大小原始碼TextView