引言
在開發react-native
App時,相信大家都應該遇到過這樣的問題:使用者設定了系統的字型大小之後,導致自己的APP佈局紊亂,甚至有些內容會被切掉/隱藏,這對於使用者來講,是非常不好的使用者體驗。
那為什麼會出現這種情況呢呢?原因是我們在開發的時候,佈局的前提是系統的字型大小設定為預設大小,所以只能保證在系統字型大小正常的情況下,我們的佈局是友好的,
那麼,我們應該如何解決這個問題呢?今天這篇文章,就給大家介紹幾種解決方案。
Text和TextInput
在react-native
中用來顯示文字的,一般會用到兩個元件:Text
和TextInput
。所以,我們只要針對這兩個元件做好工作,那就基本上解決了字型大小適配的問題
Text
和TextInput
它們有一個共同屬性:
allowFontScaling
這個屬性在react-native
官方文件中解釋如下:
Specifies whether fonts should scale to respect Text Size accessibility settings. The default is
true
.
意思是:
是否隨系統指定的文字大小變化而變化。預設值為
true
這給我提供瞭解決方案,只要我們給Text
和TextInput
的屬性allowFontScaling
設定值為false
,那麼文字大小就不會隨系統字型大小的變化而變化。
設定allowFontScaling
我們有幾種方式來設定Text
和TextInput
的allowFontScaling
。第一種:
1. 給Text
和TextInput
元件設定allowFontScaling = false
<Text allowFontScaling={false}/>
<TextInput allowFontScaling={false}/>
複製程式碼
這種方案效率很低,需要在每個使用到這兩個元件的地方都加上這個屬性。但是一般這兩個元件的使用率還是很高的,所以這是一個龐大的工作量,而且在開發過程當中,我們也很容易忘記設定它
那麼有沒有更好實現方式呢?當然有,這就是下面講的第二種:
2. 自定義MyText/MyTextInput元件
我們可以自定義一個元件MyText
, 然後統一設定allowFontScaling = false
屬性,然後在其他需要呼叫的時候,直接用MyText
代替Text
。
MyText.js
import React from 'react'
import {Text} from 'react-native'
export default class MyText extends React.Component {
render() {
return (
<Text
allowFontScaling={false}
{...this.props}>
{this.props.children}
</Text>
)
}
}
複製程式碼
這個方案足夠好了,但是,你仍然可能在某段程式碼裡,忘記使用MyText
而是直接使用Text
,這個時候,問題依然會出現。
那麼,就沒有一種萬無一失的方案嗎?當然有啦,第三種:
3. 重寫Text的render()
是的,我們可以重寫Text
的render()
方法,讓Text
在渲染的時候,設定allowFontScaling = false
。這裡,我們需要用到lodash
的wrap()
方法:
0.56(不包括)版本之前
Text.prototype.render = _.wrap(Text.prototype.render, function (func, ...args) {
let originText = func.apply(this, args)
return React.cloneElement(originText, {allowFontScaling: false})
})
複製程式碼
注意1:
在react-native
版本0.56
之前,Text
是通過React的createReactClass
方式來建立類的,也就是說,是通過javascript
的prototype
的方式來建立類。所以重寫render
方法時,需要通過Text.prototype.render
來引用
而在0.56
版本,Text
改為了es6
中extends
的實現方式來建立類,所以,需要如下方式來重寫render
:
0.56(包括)版本之後
Text.render = _.wrap(Text.render, function (func, ...args) {
let originText = func.apply(this, args)
return React.cloneElement(originText, {allowFontScaling: false})
})
複製程式碼
大家可以檢視原始碼,或者檢視0.56
的change-log
注意2: 這段程式碼最好放在你app整個元件執行呼叫之前,比如在我的專案中,我放的位置:
import React from 'react'
import {AppRegistry, Text, DeviceEventEmitter, YellowBox} from 'react-native'
import {Provider} from 'react-redux'
import App from './src/App'
import _ from 'lodash'
//字型不隨系統字型變化
Text.render = _.wrap(Text.render, function (func, ...args) {
let originText = func.apply(this, args)
return React.cloneElement(originText, {allowFontScaling: false})
})
...
...
class MyApp extends React.Component {
render() {
return (
<Provider store={store}>
<App/>
</Provider>
)
}
}
AppRegistry.registerComponent("xxx", () => MyApp);
複製程式碼
注意3:
但是很遺憾的是,這個只適用於Text
,TextInput
不能用於此方案。
那麼,有沒有一種方案,能夠同時相容Text
和TextInput
並且做到一勞永逸呢?當然有了,終極方案:
4. 完美方案:修改defaultProps
首先我們來看各種元件的原始碼.
...
getDefaultProps(): Object {
return {
allowFontScaling: true,
underlineColorAndroid: 'transparent',
};
},
...
複製程式碼
...
static defaultProps = {
accessible: true,
allowFontScaling: true,
ellipsizeMode: 'tail',
};
...
複製程式碼
通過這兩個程式碼片段可以知道,在定義Text
和TextInput
時,都有給元件設定預設屬性的操作.
所以我們可以:
TextInput.defaultProps = Object.assign({}, TextInput.defaultProps, {defaultProps: false})
Text.defaultProps = Object.assign({}, Text.defaultProps, {allowFontScaling: false})
複製程式碼
來直接設定Text
和TextInput
的allowFontScaling
屬性預設值為false
,真正實現了一勞永逸。
確保react-navigation相容
通過設定defaultProps
的方式來修改allowFontScaling
的值為false
,會有一個問題。
大家在使用react-native
時,最常用到的navigator
應該是react-navigation。你需要單獨設定headertitleallowfontscaling和allowFontScaling來確保react-navigation的tabTitle
和headerTitle
沒有問題。
結語
好了,到此,我們就完美解決了 react-native
開發中,字型大小不隨系統字型大小變化而變化 的問題。
我們總結一下:
react-native
中使用Text
和TextInput
負責顯示文字資訊Text
和TextInput
中設定allowFontScaling=false
可以讓字型大小不隨系統設定而變化- 可以通過單個元件設定、自定義元件、重寫
render()
、設定defaultProps
預設值這四種方式來設定allowFontScaling
的值為false
- 對於重寫
render()
、設定defaultProps
預設值這兩種方式,需要把設定程式碼放在app元件初始化之前。 - 確保
react-navigation
相容