ReactNative字型大小不隨系統字型大小變化而變化

Quenice發表於2018-09-28

引言

在開發react-nativeApp時,相信大家都應該遇到過這樣的問題:使用者設定了系統的字型大小之後,導致自己的APP佈局紊亂,甚至有些內容會被切掉/隱藏,這對於使用者來講,是非常不好的使用者體驗。

那為什麼會出現這種情況呢呢?原因是我們在開發的時候,佈局的前提是系統的字型大小設定為預設大小,所以只能保證在系統字型大小正常的情況下,我們的佈局是友好的,

那麼,我們應該如何解決這個問題呢?今天這篇文章,就給大家介紹幾種解決方案。

Text和TextInput

react-native中用來顯示文字的,一般會用到兩個元件:TextTextInput。所以,我們只要針對這兩個元件做好工作,那就基本上解決了字型大小適配的問題

TextTextInput它們有一個共同屬性:

allowFontScaling

這個屬性在react-native官方文件中解釋如下:

Specifies whether fonts should scale to respect Text Size accessibility settings. The default is true.

意思是:

是否隨系統指定的文字大小變化而變化。預設值為true

這給我提供瞭解決方案,只要我們給TextTextInput的屬性allowFontScaling設定值為false,那麼文字大小就不會隨系統字型大小的變化而變化。

設定allowFontScaling

我們有幾種方式來設定TextTextInputallowFontScaling。第一種:

1. 給TextTextInput元件設定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()

是的,我們可以重寫Textrender()方法,讓Text在渲染的時候,設定allowFontScaling = false。這裡,我們需要用到lodashwrap()方法:

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方式來建立類的,也就是說,是通過javascriptprototype的方式來建立類。所以重寫render方法時,需要通過Text.prototype.render來引用

而在0.56版本,Text改為了es6extends的實現方式來建立類,所以,需要如下方式來重寫render

0.56(包括)版本之後

Text.render = _.wrap(Text.render, function (func, ...args) {
    let originText = func.apply(this, args)
    return React.cloneElement(originText, {allowFontScaling: false})
})
複製程式碼

大家可以檢視原始碼,或者檢視0.56change-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: 但是很遺憾的是,這個只適用於TextTextInput不能用於此方案。

那麼,有沒有一種方案,能夠同時相容TextTextInput並且做到一勞永逸呢?當然有了,終極方案:

4. 完美方案:修改defaultProps

首先我們來看各種元件的原始碼.

TextInput.js

...
  getDefaultProps(): Object {
    return {
      allowFontScaling: true,
      underlineColorAndroid: 'transparent',
    };
  },
...
複製程式碼

Text.js

...
  static defaultProps = {
    accessible: true,
    allowFontScaling: true,
    ellipsizeMode: 'tail',
  };
...
複製程式碼

通過這兩個程式碼片段可以知道,在定義TextTextInput時,都有給元件設定預設屬性的操作.

所以我們可以:

TextInput.defaultProps = Object.assign({}, TextInput.defaultProps, {defaultProps: false})
Text.defaultProps = Object.assign({}, Text.defaultProps, {allowFontScaling: false})
複製程式碼

來直接設定TextTextInputallowFontScaling屬性預設值為false,真正實現了一勞永逸。

確保react-navigation相容

通過設定defaultProps的方式來修改allowFontScaling的值為false,會有一個問題。

大家在使用react-native時,最常用到的navigator應該是react-navigation。你需要單獨設定headertitleallowfontscalingallowFontScaling來確保react-navigationtabTitleheaderTitle沒有問題。

結語

好了,到此,我們就完美解決了 react-native開發中,字型大小不隨系統字型大小變化而變化 的問題。

我們總結一下:

  1. react-native中使用TextTextInput負責顯示文字資訊
  2. TextTextInput中設定allowFontScaling=false可以讓字型大小不隨系統設定而變化
  3. 可以通過單個元件設定、自定義元件、重寫render()、設定defaultProps預設值這四種方式來設定allowFontScaling的值為false
  4. 對於重寫render()、設定defaultProps預設值這兩種方式,需要把設定程式碼放在app元件初始化之前。
  5. 確保react-navigation相容

相關文章