引言
在開(kāi)發(fā)react-native
App時(shí),相信大家都應(yīng)該遇到過(guò)這樣的問(wèn)題:用戶(hù)設(shè)置了系統(tǒng)的字體大小之后,導(dǎo)致自己的APP布局紊亂,甚至有些內(nèi)容會(huì)被切掉/隱藏,這對(duì)于用戶(hù)來(lái)講,是非常不好的用戶(hù)體驗(yàn)。
那為什么會(huì)出現(xiàn)這種情況呢呢?原因是我們?cè)陂_(kāi)發(fā)的時(shí)候,布局的前提是系統(tǒng)的字體大小設(shè)置為默認(rèn)大小,所以只能保證在系統(tǒng)字體大小正常的情況下,我們的布局是友好的,
那么,我們應(yīng)該如何解決這個(gè)問(wèn)題呢?今天這篇文章,就給大家介紹幾種解決方案。
Text和TextInput
在react-native
中用來(lái)顯示文字的,一般會(huì)用到兩個(gè)組件:Text
和TextInput
。所以,我們只要針對(duì)這兩個(gè)組件做好工作,那就基本上解決了字體大小適配的問(wèn)題
Text
和TextInput
它們有一個(gè)共同屬性:
allowFontScaling
這個(gè)屬性在react-native
官方文檔中解釋如下:
Specifies whether fonts should scale to respect Text Size accessibility settings. The default is
true
.
意思是:
是否隨系統(tǒng)指定的文字大小變化而變化。默認(rèn)值為
true
這給我提供了解決方案,只要我們給Text
和TextInput
的屬性allowFontScaling
設(shè)置值為false
,那么文字大小就不會(huì)隨系統(tǒng)字體大小的變化而變化。
設(shè)置allowFontScaling
我們有幾種方式來(lái)設(shè)置Text
和TextInput
的allowFontScaling
。第一種:
1. 給Text
和TextInput
組件設(shè)置allowFontScaling = false
<Text allowFontScaling={false}/>
<TextInput allowFontScaling={false}/>
這種方案效率很低,需要在每個(gè)使用到這兩個(gè)組件的地方都加上這個(gè)屬性。但是一般這兩個(gè)組件的使用率還是很高的,所以這是一個(gè)龐大的工作量,而且在開(kāi)發(fā)過(guò)程當(dāng)中,我們也很容易忘記設(shè)置它
那么有沒(méi)有更好實(shí)現(xiàn)方式呢?當(dāng)然有,這就是下面講的第二種:
2. 自定義MyText/MyTextInput組件
我們可以自定義一個(gè)組件MyText
, 然后統(tǒng)一設(shè)置allowFontScaling = false
屬性,然后在其他需要調(diào)用的時(shí)候,直接用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>
)
}
}
這個(gè)方案足夠好了,但是,你仍然可能在某段代碼里,忘記使用MyText
而是直接使用Text
,這個(gè)時(shí)候,問(wèn)題依然會(huì)出現(xiàn)。
那么,就沒(méi)有一種萬(wàn)無(wú)一失的方案嗎?當(dāng)然有啦,第三種:
3. 重寫(xiě)Text的render()
是的,我們可以重寫(xiě)Text
的render()
方法,讓Text
在渲染的時(shí)候,設(shè)置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
是通過(guò)React的createReactClass
方式來(lái)創(chuàng)建類(lèi)的,也就是說(shuō),是通過(guò)javascript
的prototype
的方式來(lái)創(chuàng)建類(lèi)。所以重寫(xiě)render
方法時(shí),需要通過(guò)Text.prototype.render
來(lái)引用
而在0.56
版本,Text
改為了es6
中extends
的實(shí)現(xiàn)方式來(lái)創(chuàng)建類(lèi),所以,需要如下方式來(lái)重寫(xiě)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整個(gè)組件執(zhí)行調(diào)用之前,比如在我的項(xiàng)目中,我放的位置:
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:
但是很遺憾的是,這個(gè)只適用于Text
,TextInput
不能用于此方案。
那么,有沒(méi)有一種方案,能夠同時(shí)兼容Text
和TextInput
并且做到一勞永逸呢?當(dāng)然有了,終極方案:
4. 完美方案:修改defaultProps
首先我們來(lái)看各種組件的源碼.
...
getDefaultProps(): Object {
return {
allowFontScaling: true,
underlineColorAndroid: 'transparent',
};
},
...
...
static defaultProps = {
accessible: true,
allowFontScaling: true,
ellipsizeMode: 'tail',
};
...
通過(guò)這兩個(gè)代碼片段可以知道,在定義Text
和TextInput
時(shí),都有給組件設(shè)置默認(rèn)屬性的操作.
所以我們可以:
TextInput.defaultProps = Object.assign({}, TextInput.defaultProps, {defaultProps: false})
Text.defaultProps = Object.assign({}, Text.defaultProps, {allowFontScaling: false})
來(lái)直接設(shè)置Text
和TextInput
的allowFontScaling
屬性默認(rèn)值為false
,真正實(shí)現(xiàn)了一勞永逸。
確保react-navigation兼容
通過(guò)設(shè)置defaultProps
的方式來(lái)修改allowFontScaling
的值為false
,會(huì)有一個(gè)問(wèn)題。
大家在使用react-native
時(shí),最常用到的navigator
應(yīng)該是react-navigation。你需要單獨(dú)設(shè)置headertitleallowfontscaling和allowFontScaling來(lái)確保react-navigation的tabTitle
和headerTitle
沒(méi)有問(wèn)題。
結(jié)語(yǔ)
好了,到此,我們就完美解決了react-native
開(kāi)發(fā)中,字體大小不隨系統(tǒng)字體大小變化而變化的問(wèn)題。
我們總結(jié)一下:
-
react-native
中使用Text
和TextInput
負(fù)責(zé)顯示文字信息 -
Text
和TextInput
中設(shè)置allowFontScaling=false
可以讓字體大小不隨系統(tǒng)設(shè)置而變化 - 可以通過(guò)單個(gè)組件設(shè)置、自定義組件、重寫(xiě)
render()
、設(shè)置defaultProps
默認(rèn)值這四種方式來(lái)設(shè)置allowFontScaling
的值為false
- 對(duì)于重寫(xiě)
render()
、設(shè)置defaultProps
默認(rèn)值這兩種方式,需要把設(shè)置代碼放在app組件初始化之前。 - 確保
react-navigation
兼容