一、前言
在移动端开发中,长文本的展开收起功能是常见的交互需求。在低版本中,onTextLayout无法实现的情况下,本文将基于React Native,通过一个ExpandableText组件实现智能化的文本截断功能,并深入讲解其实现原理与性能优化技巧。
二、实现原理
2.1 核心思路
双文本渲染策略:通过隐藏的测量文本和实际显示文本分离
动态计算机制:自动检测文本行数是否超过限制
平滑动画:使用LayoutAnimation实现流畅的展开收起动画
三、代码实现解析
3.1 状态管理
const [expanded, setExpanded] = useState(false); // 展开状态
const [showMoreButton, setShowMoreButton] = useState(false); // 按钮显示
const [shouldMeasure, setShouldMeasure] = useState(true); // 测量标识
3.2 文本测量核心逻辑
useEffect(() => {
if (textRef.current && shouldMeasure) {
setTimeout(() => {
textRef.current.measure((x, y, width, height) => {
const lineHeight = textStyle?.lineHeight ||
(textStyle?.fontSize ?
textStyle?.fontSize * 1.2 : 20);
const lines = Math.ceil(height / lineHeight);
setShowMoreButton(lines > maxLines);
setShouldMeasure(false);
});
}, 100);
}
}, [text, maxLines, shouldMeasure]);
代码说明:
使用setTimeout确保测量发生在布局完成之后
动态计算行高,优先使用自定义样式中的lineHeight
通过height / lineHeight计算实际行数
3.3 动画实现
const toggleExpand = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded(!expanded);
};
四、性能优化点
测量标识控制:通过shouldMeasure状态避免重复测量
双文本渲染策略:
{/* 测量用隐藏文本 */}
<Text style={{ opacity: 0, display: shouldMeasure ? 'block' : 'none' }}>
{text}
</Text>
{/* 实际显示文本 */}
{!shouldMeasure && (
<Text numberOfLines={expanded ? undefined : maxLines}>
{text}
</Text>
)}
动态行高计算:自动适配不同字体大小和行高设置
五、使用示例
<ExpandableText
text="这是一段需要截断的长文本..."
maxLines={3}
textStyle={{
fontSize: 16,
lineHeight: 24,
color: '#333'
}}
/>
六、扩展优化建议
动态内容更新:当文本内容变化时重置测量状态
useEffect(() => {
setShouldMeasure(true);
}, [text]);
自定义按钮样式:通过props暴露按钮样式配置
<ExpandableText
buttonStyle={{ color: 'blue' }}
expandedText="收起全文"
collapsedText="显示更多"
/>
性能监控:添加测量异常处理
textRef.current.measure((x, y, width, height) => {
if (height === 0) {
console.warn('文本测量失败');
return;
}
// ...后续逻辑
});
七、总结
本文实现的展开收起组件具有以下优势:
自动适配:智能识别文本长度
流畅动画:原生动画支持
样式灵活:支持自定义文本样式
性能优化:避免不必要的重复计算
通过合理运用React Native的测量API和动画系统,我们可以创建出既美观又高性能的交互组件。该方案已在实际项目中验证,可稳定运行于iOS和Android双平台。