概述
现有的伙伴应用使用RN框架开发的历史页面多且冗杂。为达到鸿蒙原生一多体验,所有页面若均使用ArkUI原生框架重新开发耗时长、成本高,不可行。针对此问题,本文将主要提供一套RN多设备响应式组件及方案:
1)一套基于RN的鸿蒙特征动画组件库,在RN页面实现鸿蒙特征动画UI效果;
2)一套基于RN的一多高阶组件库,在RN页面实现折叠屏悬停避让分栏的UI效果;
首先介绍组件及效果说明,再分别结合组件效果提供对应场景的开发案例,最终提供示例代码指导实际开发。
组件库安装
参考rn\_multidevice\_layout\_scenepkg 和 rn\_hmfeatures。
多设备断点
断点设计原理
RN断点是基于鸿蒙多设备封装的一套断点机制,通过设置断点,让开发者可以结合窗口宽度去实现不同的页面布局效果。
断点以应用窗口宽度为基准,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,默认提供的断点区间如下所示。
多设备适配指导
在实际开发过程中,可以使用rn\_multidevice\_layout\_scenepkg的setBreakpoints自定义断点的区间,也可以使用上述默认的断点区间。使用useBreakpointValue时,只需将屏幕断点所对应的参数传入useBreakpointValue,当屏幕断点发生变化时,该hook会根据当前断点的类型返回所对应的数据。
具体示例如下所示:
自定义断点区间
// 自定义断点区间,可选
useEffect(() => {
setBreakpoints({
base: 320,
md: 768,
lg: 1024,
});
});
使用断点hook并获取不同断点下的属性值
const color = useBreakpointValue({
base: 'red',
xs: 'blue',
sm: 'green',
md: 'yellow',
lg: 'purple',
xl: 'orange',
});
return (
<Text style={{ color }}>Responsive Color Text</Text>
);
RN鸿蒙特征动画组件
组件使用说明
rn\_hmfeatures鸿蒙特征动画组件GeometryView,基于ArkUI的geometryTransition接口,实现了鸿蒙特征动画效果,详细介绍可参考:使用geometryTransition共享元素转场。
组件导入方式
import GeometryView from 'rn_hmfeatures/src/';
组件API
场景案例
点击歌单页当前播放音乐控件,跳转音乐播放页,触发一镜到底的转场效果。
关键代码片段
1、将歌单页中的当前播放音乐控件设置共享元素ID,监听点击事件并发送消息至ArkUI侧。(SampleTurboModule为自定义TurboModule,执行调用ArkUI侧发送消息方法)
return (
<GeometryView
style={styles.container}
geometryViewID={'test'} // 设置共享元素ID
onGeometryViewClick={() => {
SampleTurboModule.pushStringToHarmony('pages/MusicPlay', 1);
}}>
<Image source={require('../../../../asset/cover.png')} style={[styles.albumCover, { marginLeft: itemLeft }]} />
<View style={styles.songInfo}>
<Text style={styles.songTitle}>{song.title}</Text>
<Text style={styles.songArtist}>{song.artist}</Text>
</View>
<View style={{ flex: 1 }} />
{controlIcons}
</GeometryView>
);
2、ArkUI侧监听跳转事件,在animateTo闭包内执行页面跳转逻辑
aboutToAppear() {
emitter.on({ eventId: 1 }, () => {
animateTo({ duration: 700, curve: Curve.Friction }, () => {
this.navPathStack.pushPath({ name: 'MusicPlayPage' });
});
});
}
3、音乐播放页设置与歌单页一致的共享元素ID
@Component
export default struct MusicPlayPage {
private instance: RNInstance = LoadManager.instance;
private bundlePath = 'bundle/musicplay.harmony.bundle';
private moduleName = 'MusicPlay';
@StorageLink('isMetroAvailable') isMetroAvailable: boolean = false;
@Consume('navPathStack') navPathStack: NavPathStack;
aboutToAppear() {
emitter.on({ eventId: 2 }, () => {
animateTo({ duration: 700, curve: Curve.Friction }, () => {
this.navPathStack.pop();
});
});
}
aboutToDisappear() {
emitter.off(2);
}
build() {
NavDestination() {
if (this.isMetroAvailable) {
MetroBaseRN({
moduleName: this.moduleName,
})
.align(Alignment.Top)
} else if (this.instance) {
BaseRN({
rnInstance: this.instance,
moduleName: this.moduleName,
bundlePath: this.bundlePath,
}).align(Alignment.Top)
}
}
.geometryTransition('test') // 设置共享元素ID
.hideTitleBar(true)
}
}
RN折叠屏适配组件
组件使用说明
RN折叠屏适配组件FoldSplitContainer,包含primary、secondary、extra三个区域,实现折叠屏二分栏、三分栏在展开态、悬停态以及折叠屏悬停状态下折痕区避让,详情参考:rn\_multidevice\_layout\_scenepkg。
组件导入方式
import { FoldSplitContainer } from 'rn_multidevice_layout_scenepkg/src';
FoldSplitContainer组件
ExpandedRegionLayoutOptions
HoverModeRegionLayoutOptions
FoldedRegionLayoutOptions
onHoverStatusChangeHandler
HoverModeStatus
ExtraRegionPosition
PresetSplitRatio
推荐使用方式
const primaryRender = () => (
<View>
<Text> 此区域为primary
</View>
);
const secondRender = () => (
<View>
<Text> 此区域为second
</View>
);
const extraRender = () => (
<View>
<Text> 此区域为extra
</View>
);
const expandedLayoutOptions: ExpandedRegionLayoutOptions = {
isExtraRegionPerpendicular: true,
verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
horizontalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
};
const foldedRegionLayoutOptions: FoldedRegionLayoutOptions = {
verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
};
const hoverModeLayoutOptions: HoverModeRegionLayoutOptions = {
horizontalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
showExtraRegion: true,
};
<FoldSplitContainer
primary={primaryRender()}
secondary={secondRender()}
extra={extraRender()}
expandedLayoutOptions={expandedLayoutOptions}
foldedLayoutOptions={foldedRegionLayoutOptions}
hoverModeLayoutOptions={hoverModeLayoutOptions}
/>
场景案例
实现效果
关键代码片段
这段代码是用React Native编写的,用于构建一个音乐播放器界面:
secondRender函数:
- 这个函数返回一个渲染组件,该组件包含多个View和Image组件,用于显示音乐的封面、标题、艺术家名、播放进度条、控制按钮(如上一曲、下一曲、播放/暂停、重复等)和一些额外的图标。
- View组件用于布局,Image组件用于显示图标或音乐封面。
- Slider组件用于显示播放进度条,用户可以通过拖动来改变播放位置。
- TouchableOpacity组件用于创建可点击的图标。
extraRender函数:
- 这个函数用于显示歌词信息。
布局选项:
- expandedLayoutOptions、foldedRegionLayoutOptions和hoverModeLayoutOptions定义了不同的布局选项,用于控制组件在展开、折叠和悬停模式下的布局。
返回的组件:
- 最后,代码返回一个ImageBackground组件,该组件使用一个模糊背景图片,并在其上层叠加FoldSplitContainer组件。FoldSplitContainer组件使用前面定义的primaryRender、secondRender和extraRender函数来渲染其主要、次要和额外的内容部分。
代码示例如下:
const secondRender = () => (
<View style={{flex: 1, alignItems: 'center'}}>
<View style={styles.message}>
<View>
<Text style={styles.title}>{title}</Text>
<Text style={styles.artist}>{artist}</Text>
</View>
<Image
source={require('../../../asset/likes.svg')}
style={styles.imageGrey}
/>
</View>
<View style={styles.slider}>
<Slider
style={{width: '100%'}}
minimumValue={0}
maximumValue={duration}
value={position}
minimumTrackTintColor="#e8e1e1"
maximumTrackTintColor="#784949"
thumbStyle={{opacity: 0}}
onValueChange={(val: number) => {
seekTo(val);
}}
/>
</View>
<View style={styles.controls}>
<Text style={styles.text}>{formatTime(position)}</Text>
<Text style={styles.text}>{formatTime(duration)}</Text>
</View>
<View style={styles.container}>
<Image
source={require('../../../asset/repeat.svg')}
style={styles.imageGrey}
/>
<TouchableOpacity onPress={skipToPrevious}>
<Image
source={require('../../../asset/left.svg')}
style={styles.image}
/>
</TouchableOpacity>
<TouchableOpacity onPress={togglePlayPause}>
<Image
source={
playState === State.Playing
? require('../../../asset/pause.svg')
: require('../../../asset/play.svg')
}
style={styles.imagePlay}
/>
</TouchableOpacity>
<TouchableOpacity onPress={skipToNext}>
<Image
source={require('../../../asset/forward_end_fill.svg')}
style={styles.image}
/>
</TouchableOpacity>
<Image
source={require('../../../asset/music_note_list.svg')}
style={styles.imageGrey}
/>
</View>
<View style={styles.container}>
<Image
source={require('../../../asset/share_play.svg')}
style={styles.imageGrey}
/>
<Image
source={require('../../../asset/bell.svg')}
style={styles.imageGrey}
/>
<Image
source={require('../../../asset/arrow_down_circle.svg')}
style={styles.imageGrey}
/>
<Image
source={require('../../../asset/dot.svg')}
style={styles.imageGrey}
/>
</View>
</View>
);
const extraRender = () => (
<View style={styles.extra}>
<Text
style={{
marginTop: isHover ? 70 : 0,
marginRight: isPad ? 200 : 0,
fontSize: 23,
color: '#ffffff',
}}>
此歌曲为纯音乐,请您欣赏
</Text>
</View>
);
const expandedLayoutOptions: ExpandedRegionLayoutOptions = {
isExtraRegionPerpendicular: true,
verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
horizontalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
};
const foldedRegionLayoutOptions: FoldedRegionLayoutOptions = {
verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
};
const hoverModeLayoutOptions: HoverModeRegionLayoutOptions = {
horizontalSplitRatio: 0.66,
showExtraRegion: true,
};
return (
<ImageBackground
source={require('../../../asset/blur.png')}
style={{width: '100%', height: '100%'}}>
<View style={{position: 'absolute', width: '100%', alignItems: 'center'}}>
<FoldSplitContainer
primary={primaryRender()}
secondary={secondRender()}
extra={extraRender()}
expandedLayoutOptions={expandedLayoutOptions}
foldedLayoutOptions={foldedRegionLayoutOptions}
hoverModeLayoutOptions={hoverModeLayoutOptions}
/>
</ImageBackground>
);
示例代码
基于RN框架的多设备开发sample示例代码地址:https://gitee.com/openharmony-sig/rn\_multidevice\_layout\_scenepkg
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。