引言
在開發(fā)react-nativeApp時,相信大家都應該遇到過這樣的問題:用戶設置了系統(tǒng)的字體大小之后,導致自己的APP布局紊亂,甚至有些內(nèi)容會被切掉/隱藏,這對于用戶來講,是非常不好的用戶體驗。
那為什么會出現(xiàn)這種情況呢呢?原因是我們在開發(fā)的時候,布局的前提是系統(tǒng)的字體大小設置為默認大小,所以只能保證在系統(tǒng)字體大小正常的情況下,我們的布局是友好的,
那么,我們應該如何解決這個問題呢?今天這篇文章,就給大家介紹幾種解決方案。
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.
意思是:
是否隨系統(tǒng)指定的文字大小變化而變化。默認值為
true
這給我提供了解決方案,只要我們給Text和TextInput的屬性allowFontScaling設置值為false,那么文字大小就不會隨系統(tǒng)字體大小的變化而變化。
設置allowFontScaling
我們有幾種方式來設置Text和TextInput的allowFontScaling。第一種:
1. 給Text和TextInput組件設置allowFontScaling = false
<Text allowFontScaling={false}/>
<TextInput allowFontScaling={false}/>
這種方案效率很低,需要在每個使用到這兩個組件的地方都加上這個屬性。但是一般這兩個組件的使用率還是很高的,所以這是一個龐大的工作量,而且在開發(fā)過程當中,我們也很容易忘記設置它
那么有沒有更好實現(xiàn)方式呢?當然有,這就是下面講的第二種:
2. 自定義MyText/MyTextInput組件
我們可以自定義一個組件MyText, 然后統(tǒng)一設置allowFontScaling = false屬性,然后在其他需要調(diào)用的時候,直接用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,這個時候,問題依然會出現(xiàn)。
那么,就沒有一種萬無一失的方案嗎?當然有啦,第三種:
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方式來創(chuàng)建類的,也就是說,是通過javascript的prototype的方式來創(chuàng)建類。所以重寫render方法時,需要通過Text.prototype.render來引用
而在0.56版本,Text改為了es6中extends的實現(xiàn)方式來創(chuàng)建類,所以,需要如下方式來重寫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整個組件執(zhí)行調(diào)用之前,比如在我的項目中,我放的位置:
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'
//字體不隨系統(tǒng)字體變化
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,真正實現(xiàn)了一勞永逸。
確保react-navigation兼容
通過設置defaultProps的方式來修改allowFontScaling的值為false,會有一個問題。
大家在使用react-native時,最常用到的navigator應該是react-navigation。你需要單獨設置headertitleallowfontscaling和allowFontScaling來確保react-navigation的tabTitle和headerTitle沒有問題。
結語
好了,到此,我們就完美解決了react-native開發(fā)中,字體大小不隨系統(tǒng)字體大小變化而變化的問題。
我們總結一下:
-
react-native中使用Text和TextInput負責顯示文字信息 -
Text和TextInput中設置allowFontScaling=false可以讓字體大小不隨系統(tǒng)設置而變化 - 可以通過單個組件設置、自定義組件、重寫
render()、設置defaultProps默認值這四種方式來設置allowFontScaling的值為false - 對于重寫
render()、設置defaultProps默認值這兩種方式,需要把設置代碼放在app組件初始化之前。 - 確保
react-navigation兼容