需求背景:
最近遇到一个问题,就是在FlatList中显示一行行抄表数据,每行的左侧文本是抄表时间,右侧文本是抄表值。抄表时间是固定格式、固定宽度的 ,但是右侧的抄表值是变长的,从个位数到8、9位数不等,值的结尾还显示单位,所以数据过长时会出现换行或是省略号显示的效果。
产品经理要求是抄表值一行显示不要换行,也不要显示省略号,完整显示。当抄表值过大在布局中显示不下时,则动态缩小字体大小,来适应布局宽度,在一行内完整显示抄表值。
实现效果如下:
下图demo中文本的宽度填充屏幕,点击按钮会动态追加文本。
限制一行显示效果:
限制两行显示效果:
上代码:
import React, { useState } from 'react';
import type { Node } from 'react';
import {
Button,
Platform,
SafeAreaView,
Text,
NativeSyntheticEvent,
TextLayoutEventData,
} from 'react-native';
import {
Colors,
} from 'react-native/Libraries/NewAppScreen';
export interface AdjustTextProps {
children: any;
initFontSize: number;
numberOfLines: number;
}
const AdjustText: () => Node = (props: AdjustTextProps) => {
const {
children, numberOfLines, initFontSize,
} = props;
const [currentFontSize, setCurrentFontSize] = useState(initFontSize);
AdjustText.defaultProps = {
numberOfLines: 1,
};
const onTextLayout = (e: NativeSyntheticEvent<TextLayoutEventData>) => {
const { lines } = e.nativeEvent;
if (lines.length > numberOfLines) {
setCurrentFontSize(currentFontSize - 0.5);
}
};
return (
<Text
{...props}
style={[props?.style, { fontSize: currentFontSize }]}
numberOfLines={numberOfLines}
adjustsFontSizeToFit={Platform.OS === 'ios'}
onTextLayout={Platform.OS === 'ios' ? null : onTextLayout}
>
{children}
</Text>
);
};
const App: () => Node = () => {
const [text, setText] = useState('Hello World.');
return (
<SafeAreaView style={{ height: '100%', justifyContent: 'center', backgroundColor: Colors.darker }}>
<AdjustText
initFontSize={20}
numberOfLines={1}
style={{ color: 'black', backgroundColor: 'pink', marginBottom: 20 }}
>
{text}
</AdjustText>
<Button
title={'Append Text'}
onPress={() => setText(`${text} Hello World.`)}
/>
</SafeAreaView>
);
};
export default App;
代码解析:
App函数组件是页面容器。
AdjustText
是封装的字体大小自适应函数组件,是对Text组件做了一层再包裹。其属性同Text组件,但是多了一个initFontSize
属性,是文本的默认字体大小,当文本在指定行数中放不下时,文本会基于这个字体大小向下缩小。
因为在iOS上自带属性adjustsFontSizeToFit
已经实现了字体自动适配,不需要我们自己处理,所以判断为iOS时,该值为true
。
Android上则没有提供类似属性,所以用到了onTextLayout
来监听文本行数的变化,当要显示的行数超过指定行数使,则触发字体大小减少0.5
的逻辑直至实现指定行数显示(这个值可以自己设置,比如1
也可以)。
onTextLayout
中的属性值lines
是一个数组,当文本为两行显示时,数组长度就为2了。
还有react native的bug
需要注意,在安卓上,当设置adjustsFontSizeToFit={true}
时,会造成一直触发onTextLayout
,文本将无限的更改布局。这个问题在react-native 0.67.3
上得到修复。