ReactNative字型大小不隨系統字型大小變化而變化
在開發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 istrue
.
意思是:
是否隨系統指定的文字大小變化而變化。預設值為true
這給我提供瞭解決方案,只要我們給Text
和TextInput
的屬性allowFontScaling
設定值為true
,那麼文字大小就不會隨系統字型大小的變化而變化。
設定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
。這裡,我們需要用到ofollow,noindex">
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
相容