在鸿蒙设备上使用 React Native 开发时,Dimensions 是最常用但也最容易踩坑的 API 之一。很多开发者刚上手就把 Dimensions.get('window').width 写在 StyleSheet 里,结果横竖屏切换后布局错乱。本文基于 HarmonyOS 6.0 + RNOH 0.84.1,总结 Dimensions 在鸿蒙上的正确用法、防抖处理、以及折叠屏、分屏等特殊场景适配经验。
一、get 方法:window 与 screen 的区别
Dimensions.get('window') 返回应用可见区域(不含状态栏、导航栏),而 Dimensions.get('screen') 返回完整物理屏幕尺寸。以某款鸿蒙手机为例:window.height 为 780,screen.height 为 852,差值 72px 就是系统 UI 高度。
- import { Dimensions } from 'react-native';
- const { width: wW, height: wH } = Dimensions.get('window');
- const { width: sW, height: sH } = Dimensions.get('screen');
复制代码
注意:不要在 StyleSheet 中缓存 Dimensions.get() 的返回值,因为 StyleSheet 创建的是静态对象,横竖屏切换后不会更新。
二、useWindowDimensions:推荐的 Hook 方案
函数组件优先使用 useWindowDimensions,它自动订阅 change 事件,组件卸载时自动取消订阅。
- import { useWindowDimensions } from 'react-native';
- const AdaptiveLayout = () => {
- const { width, height, scale, fontScale } = useWindowDimensions();
- // 根据 width 响应式布局
- };
复制代码
如果需要兼容类组件,可以手动监听 Dimensions.addEventListener('change'),并在 componentWillUnmount 中移除。
三、鸿蒙上的特有坑
坑 1:change 事件触发时机不稳定
鸿蒙设备上快速旋转时,change 事件可能先触发但 window 宽高值还未更新,导致布局闪烁。建议加防抖处理,延迟 150-200ms 再更新状态。
- const debounceTimer = useRef(null);
- useEffect(() => {
- const sub = Dimensions.addEventListener('change', ({ window }) => {
- clearTimeout(debounceTimer.current);
- debounceTimer.current = setTimeout(() => {
- setDims({ width: window.width, height: window.height });
- }, 150);
- });
- return () => sub.remove();
- }, []);
复制代码
坑 2:fontScale 不准确
鸿蒙系统字体缩放设置与 RN 的 fontScale 返回值可能不一致。实测系统字体调到最大后,fontScale 仍返回 1.0。建议在应用内固定字体大小,或限制缩放范围。
- const limitedFontScale = Math.min(Math.max(fontScale, 0.85), 1.15);
复制代码
坑 3:window 与 screen 高度差因设备而异
不同鸿蒙设备的系统 UI 高度不同,差异范围 50-100px。如果做全屏场景(如游戏),需注意 screen 高度可能被系统栏遮挡。
坑 4:模拟器与真机行为不一致
鸿蒙模拟器上横竖屏切换表现正常,但真机上 change 事件触发频率更高,容易打断动画过渡。务必在真机上进行横竖屏适配测试。
坑 5:折叠屏展开/折叠时尺寸变化
折叠屏设备展开后宽高比剧烈变化,change 事件会触发但可能分步到达。通过 aspectRatio 判断当前折叠状态,并采用防抖避免频繁重排。
- const FoldableAdapter = () => {
- const { width, height } = useWindowDimensions();
- const [isFolded, setIsFolded] = useState(true);
- useEffect(() => {
- const sub = Dimensions.addEventListener('change', ({ window }) => {
- setIsFolded(window.width / window.height < 1.5);
- });
- return () => sub.remove();
- }, []);
- };
复制代码
四、分屏适配与性能优化
鸿蒙分屏后 change 事件可能多次触发,每次携带的宽度值分步变化。同样需要防抖(建议 200ms),等尺寸稳定后再更新布局。
- const SplitScreenAdapter = () => {
- const [columns, setColumns] = useState(1);
- useEffect(() => {
- let timer;
- const sub = Dimensions.addEventListener('change', ({ window }) => {
- clearTimeout(timer);
- timer = setTimeout(() => {
- setColumns(window.width > 600 ? 2 : 1);
- }, 200);
- });
- return () => { sub.remove(); clearTimeout(timer); };
- }, []);
- };
复制代码
若页面有大量计算或复杂动画,使用 useMemo 缓存布局结果,避免每次渲染都重新计算。
五、最佳实践总结
- 优先使用 useWindowDimensions,避免手动管理订阅。
- 不要在 StyleSheet 中调用 Dimensions.get()。
- 横竖屏切换、折叠屏、分屏场景都加防抖(150-200ms)。
- 鸿蒙上 fontScale 不可靠,建议在应用内控制字体大小。
- 旋转锁定开启时 change 事件不会触发,需提供手动旋转按钮或检测系统设置。
- 所有横竖屏适配逻辑必须在真机上验证,模拟器结果不可信。
- 如果首次调用 Dimensions.get() 返回 0,确保在 useEffect 或 onLayout 中获取。
最后提醒:鸿蒙 RN 生态仍在快速迭代,本文示例均基于 HarmonyOS 6.0 与 RNOH 0.84.1 测试通过。遇到新问题请及时查阅官方文档或社区讨论。 |