React Native鸿蒙跨平台使用Date.now()生成唯一ID,展开运算符确保不可变性,map操作实现精确更新,条件表达式处理点赞/取消点赞逻辑
摘要 本文详细分析了基于React Native构建的动态留言板应用架构,重点探讨了其核心交互设计与跨平台适配方案。系统采用Animated动画引擎实现点赞弹跳效果(使用sequence组合动画和原生驱动),通过状态管理实现动态列表更新与滚动控制。在鸿蒙适配层,需要重构动画逻辑为ArkUI的animateTo方案,并确保虚拟化列表、输入法事件和弹窗行为的一致性。文章对比了React Native与
概览
- 页面构成:输入区(发布动态)、FlatList 虚拟化的留言列表、卡片内的点赞/评论/分享操作,以及顶部帮助与底部导航。
- 架构风格:函数式组件 + 本地状态,事件驱动数据更新,动效用 Animated 搭配原生驱动实现“轻量反馈”。
- 跨端关注点:动画原生驱动映射到 ArkUI、FlatList 虚拟化滚动手感与事件窗口统一、Alert 弹窗行为与返回手势管理、emoji 图标渲染一致性,以及 TextInput 的输入法合成事件(composition)处理。
关键组件:MessageItem 的动效与展开逻辑
- 点赞缩放动效采用 Animated.sequence,使用 useNativeDriver:true 在 transform 维度执行,性能友好且跨端稳定。鸿蒙端通过适配层映射到 ArkUI 原生动画能力,避免布局或颜色动画造成掉帧。
- 文本折叠/展开通过 numberOfLines 与 isExpanded 状态切换,超过阈值(长度 > 100)显示“展开/收起”操作,卡片层次简单且易复用。
- 操作菜单以 Alert 占位执行“编辑/删除/取消”,生产场景应改为统一的 ActionSheet/自定义弹窗,确保三端一致的视觉与交互(鸿蒙端桥接 ArkUI 弹窗能力与返回手势)。
示例动效逻辑(结构抽象):
const scaleValue = useRef(new Animated.Value(1)).current
Animated.sequence([
Animated.timing(scaleValue, { toValue: 1.2, duration: 100, useNativeDriver: true }),
Animated.timing(scaleValue, { toValue: 1, duration: 100, useNativeDriver: true }),
]).start()
列表与性能:FlatList 的虚拟化与滚动控制
- 列表使用 FlatList 渲染,并通过 ref + scrollToIndex 在发布后滚动到顶部,提升“发布即刻可见”的体验。事件触发后用 setTimeout 微延迟,防止尚未布局完成导致滚动失败;生产可改为 InteractionManager.runAfterInteractions 或 onContentSizeChange 的更稳策略。
- keyExtractor 使用稳定 id,renderItem 承载 MessageItem;生产建议将 renderItem 用 useCallback 缓存、MessageItem 使用 React.memo 降低重渲染,尤其在点赞计数快速变化时。
- 复杂布局下可以增加 getItemLayout(已知高度时)提升初次滚动的稳定性;鸿蒙端需确保虚拟化与滚动物理(回弹/阻尼/惯性)统一映射到 ArkUI,保证触发窗口与手感一致。
状态更新与一致性:点赞/编辑/删除/发布
- 点赞逻辑:基于 liked 状态切换 likes 计数,使用不可变更新 map 替换目标项,语义清晰;在高并发快速点击场景建议改为函数式更新 setMessages(prev => …) 保证最新状态源。
- 编辑逻辑:将目标消息文本回填到输入框并从列表中移除,实现“取出-修改-再发布”的直观流程;生产需保留原消息 id 或提供“更新”接口,避免打断历史序列。
- 删除操作:Alert 确认后过滤删除;若存在撤销需求,可引入临时缓存与定时器实现“撤销删除”模式。
示例函数式更新(提升一致性):
setMessages(prev =>
prev.map(msg => msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
)
)
输入法与粘贴:TextInput 的跨端差异治理
- 多行 TextInput(multiline + numberOfLines)在 iOS/Android/鸿蒙存在差异:键盘弹出时的滚动调整、输入法合成事件与光标定位需要在适配层正确映射到 RN 的 onChangeText/onSelectionChange,避免中文输入中间态被误判为文本完成。
- 粘贴/复制操作当前以 Alert 占位;生产需桥接系统剪贴板(iOS UIPasteboard、Android ClipboardManager、鸿蒙 ArkUI/Ability 对应能力),并处理权限与前后台切换的一致性;建议复制/粘贴统一使用纯文本,避免富文本导致统计或渲染异常。
图标与视觉一致性:emoji 的替代方案
- 图标采用 emoji(👍、🔗、☰ 等)快速原型化,但在不同系统字体下基线与颜色呈现差异明显;生产需迁移到统一矢量/字体图标栈,并在鸿蒙端通过 ArkUI 渲染能力保持像素级一致。
- 点赞的“红色高亮”与计数颜色切换,从语义上增强反馈;为避免无障碍用户的色彩识别歧义,建议同时加入 aria-like 或文本提示以增强可访问性。
弹窗与返回手势:Alert 的统一替换
- Alert 在三端的视觉与按钮排列受平台控制,难以统一风格;建议用 RN Modal/Portal 实现自定义弹窗,管控层级、焦点与返回手势,并在适配层桥接 ArkUI 弹窗能力。
- 底部导航的选中态通过样式标记,不涉及路由;生产建议接入跨端导航框架并统一返回手势与转场动画。
鸿蒙跨端适配要点
- 动画:transform 动画原生驱动(useNativeDriver:true)在鸿蒙端需映射到 ArkUI 动画能力;避免 layout/颜色动画造成高开销。
- 虚拟化与滚动:FlatList 与 SectionList 的虚拟化、滚动物理与事件窗口必须在适配层统一,确保 scrollToIndex 等方法语义一致。
- 输入法:中文 IME 合成事件正确传递,防止中间态统计抖动与光标错乱;粘贴/复制能力使用 ArkUI/Ability 桥接。
- 弹窗:自定义 Modal 统一视觉与行为,返回手势与焦点导航在鸿蒙端匹配 ArkUI 规范。
- 图标:避免 emoji 差异,使用统一图标栈,颜色与字号在不同端做最小差异化处理。
React Native动态留言板系统与鸿蒙跨端适配技术深度解析
概述
本文分析的是一个基于React Native构建的动态留言板应用,集成了实时互动、动画效果、列表管理等核心功能。该应用采用了Animated动画系统、复杂的状态管理和动态列表交互,展现了社交互动类应用的典型技术架构。在鸿蒙OS的跨端适配场景中,这种涉及复杂动画和实时交互的应用具有重要的技术参考价值。
核心架构设计深度解析
Animated动画交互系统
应用在MessageItem组件中实现了精细的动画交互:
const MessageItem = ({ message, onLike, onDelete, onEdit, onReply }) => {
const [isExpanded, setIsExpanded] = useState(false);
const scaleValue = useRef(new Animated.Value(1)).current;
const handleLike = () => {
// 序列动画:先放大后缩小
Animated.sequence([
Animated.timing(scaleValue, {
toValue: 1.2,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(scaleValue, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}),
]).start();
onLike(message.id);
};
return (
<Animated.View style={[styles.messageItem, { transform: [{ scale: scaleValue }] }]}>
{/* 组件内容 */}
</Animated.View>
);
};
这种动画设计采用了Animated.sequence组合动画,实现了点赞时的弹跳效果。useNativeDriver设置为true启用原生驱动,确保动画的流畅性能。transform scale动画通过useRef保持动画值的持久性。
在鸿蒙ArkUI体系中,动画实现需要完全重构:
@Component
struct MessageItem {
@Prop message: Message;
@Event onLike: (id: string) => void;
@State scaleValue: number = 1;
handleLike() {
// 使用animateTo实现序列动画
animateTo({
duration: 100,
onFinish: () => {
this.scaleValue = 1.2;
animateTo({
duration: 100,
onFinish: () => {
this.scaleValue = 1;
this.onLike(this.message.id);
}
});
}
});
}
build() {
// 应用缩放变换
Column()
.scale({ x: this.scaleValue, y: this.scaleValue })
}
}
动态列表管理机制
应用采用了复杂的动态列表状态管理:
const [messages, setMessages] = useState<Message[]>([]);
// 添加新消息
const handleSend = () => {
const newMessage: Message = {
id: Date.now().toString(),
text: inputText,
timestamp: '刚刚',
likes: 0,
comments: 0,
user: '我',
avatar: '??',
liked: false
};
setMessages([newMessage, ...messages]); // 新消息置顶
setInputText('');
// 滚动到顶部
setTimeout(() => {
flatListRef.current?.scrollToIndex({ index: 0, animated: true });
}, 100);
};
// 点赞逻辑
const handleLike = (id: string) => {
setMessages(messages.map(msg =>
msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
));
};
这种状态管理体现了多个重要原则:使用Date.now()生成唯一ID,展开运算符确保不可变性,map操作实现精确更新,条件表达式处理点赞/取消点赞逻辑。
鸿蒙的状态管理采用直接赋值模式:
@State messages: Message[] = [];
// 添加消息
handleSend() {
const newMessage: Message = {
id: Date.now().toString(),
text: this.inputText,
// ...其他字段
};
this.messages = [newMessage, ...this.messages];
this.inputText = '';
}
// 点赞处理
handleLike(id: string) {
this.messages = this.messages.map(msg =>
msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
);
}
智能文本展示系统
MessageItem实现了自适应的文本展示控制:
const [isExpanded, setIsExpanded] = useState(false);
<Text style={styles.messageText} numberOfLines={isExpanded ? undefined : 3}>
{message.text}
</Text>
{message.text.length > 100 && (
<TouchableOpacity onPress={() => setIsExpanded(!isExpanded)}>
<Text style={styles.expandText}>
{isExpanded ? '收起' : '展开'}
</Text>
</TouchableOpacity>
)}
这种设计通过numberOfLines属性控制文本行数,基于文本长度动态显示展开/收起按钮,提供了良好的用户体验。
鸿蒙的实现需要使用文本组件的maxLines属性:
@State isExpanded: boolean = false;
build() {
Column() {
Text(this.message.text)
.maxLines(this.isExpanded ? undefined : 3)
if (this.message.text.length > 100) {
Button(this.isExpanded ? '收起' : '展开', { type: ButtonType.Normal })
.onClick(() => this.isExpanded = !this.isExpanded)
}
}
}
跨端适配技术方案
组件映射策略
| React Native组件 | 鸿蒙ArkUI组件 | 关键适配点 |
|---|---|---|
| Animated.View | 普通组件+animateTo | 动画系统完全重构 |
| FlatList | List | 列表实现和滚动控制差异 |
| TouchableOpacity | Button | 交互反馈机制不同 |
| TextInput | TextInput | 多行文本属性一致 |
动画系统迁移
React Native的Animated模块与鸿蒙动画API对比:
| 功能 | React Native | 鸿蒙ArkUI | 适配说明 |
|---|---|---|---|
| 基础动画 | Animated.timing | animateTo | 参数配置不同 |
| 序列动画 | Animated.sequence | 嵌套animateTo | 实现方式差异 |
| 原生驱动 | useNativeDriver | 默认启用 | 性能优化策略 |
| 变换动画 | transform style | .scale()方法 | 语法差异 |
状态管理迁移
复杂状态对象的更新策略:
// React Native
setMessages(messages.map(msg =>
msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
));
// 鸿蒙
this.messages = this.messages.map(msg =>
msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
);
性能优化与最佳实践
列表性能优化
使用keyExtractor和getItemLayout提升列表性能:
<FlatList
ref={flatListRef}
data={messages}
keyExtractor={item => item.id}
getItemLayout={(data, index) => ({
length: 120, // 预估行高
offset: 120 * index,
index,
})}
renderItem={({ item }) => <MessageItem message={item} />}
/>
动画性能优化
启用useNativeDriver提升动画性能:
Animated.timing(scaleValue, {
toValue: 1.2,
duration: 100,
useNativeDriver: true, // 启用原生驱动
}).start();
完整代码演示:
// app.tsx
import React, { useState, useRef } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, FlatList, Animated } from 'react-native';
// 图标库
const ICONS = {
message: '💬',
send: '🚀',
delete: '🗑️',
edit: '✏️',
like: '👍',
comment: '💬',
share: '🔗',
menu: '☰',
};
const { width } = Dimensions.get('window');
// 留言类型
type Message = {
id: string;
text: string;
timestamp: string;
likes: number;
comments: number;
user: string;
avatar: string;
liked: boolean;
};
// 留言项组件
const MessageItem = ({
message,
onLike,
onDelete,
onEdit,
onReply
}: {
message: Message;
onLike: (id: string) => void;
onDelete: (id: string) => void;
onEdit: (id: string) => void;
onReply: (id: string) => void;
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const scaleValue = useRef(new Animated.Value(1)).current;
const handleLike = () => {
// 动画效果
Animated.sequence([
Animated.timing(scaleValue, {
toValue: 1.2,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(scaleValue, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}),
]).start();
onLike(message.id);
};
return (
<Animated.View style={[styles.messageItem, { transform: [{ scale: scaleValue }] }]}>
<View style={styles.messageHeader}>
<View style={styles.userAvatar}>
<Text style={styles.avatarText}>{message.avatar}</Text>
</View>
<View style={styles.userInfo}>
<Text style={styles.userName}>{message.user}</Text>
<Text style={styles.timestamp}>{message.timestamp}</Text>
</View>
<TouchableOpacity
style={styles.menuButton}
onPress={() => Alert.alert('操作', '选择操作', [
{ text: '编辑', onPress: () => onEdit(message.id) },
{ text: '删除', onPress: () => onDelete(message.id) },
{ text: '取消', style: 'cancel' }
])}
>
<Text style={styles.menuText}>{ICONS.menu}</Text>
</TouchableOpacity>
</View>
<View style={styles.messageContent}>
<Text style={styles.messageText} numberOfLines={isExpanded ? undefined : 3}>
{message.text}
</Text>
{message.text.length > 100 && (
<TouchableOpacity onPress={() => setIsExpanded(!isExpanded)}>
<Text style={styles.expandText}>
{isExpanded ? '收起' : '展开'}
</Text>
</TouchableOpacity>
)}
</View>
<View style={styles.messageActions}>
<TouchableOpacity style={styles.actionButton} onPress={handleLike}>
<Text style={[styles.actionIcon, message.liked && styles.liked]}>{ICONS.like}</Text>
<Text style={[styles.actionText, message.liked && styles.likedText]}>{message.likes}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton} onPress={() => onReply(message.id)}>
<Text style={styles.actionIcon}>{ICONS.comment}</Text>
<Text style={styles.actionText}>{message.comments}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Text style={styles.actionIcon}>{ICONS.share}</Text>
<Text style={styles.actionText}>分享</Text>
</TouchableOpacity>
</View>
</Animated.View>
);
};
// 主页面组件
const MessageBoardDynamicListApp: React.FC = () => {
const [messages, setMessages] = useState<Message[]>([
{
id: '1',
text: '今天天气真好,出去散散步感觉整个人都精神了!阳光明媚,微风轻拂,正是户外活动的好时机。大家有什么好的运动推荐吗?',
timestamp: '10分钟前',
likes: 12,
comments: 3,
user: '运动达人',
avatar: '🏃♂️',
liked: false
},
{
id: '2',
text: '昨天完成了人生第一次半程马拉松,虽然累但成就感满满!感谢一路上朋友们的加油打气,让我坚持到了最后。',
timestamp: '30分钟前',
likes: 24,
comments: 8,
user: '跑步爱好者',
avatar: '🏃♀️',
liked: true
},
{
id: '3',
text: '新买的运动装备终于到了,迫不及待想要试一试!这套装备颜值很高,而且据说功能性也很强,期待明天的晨跑体验。',
timestamp: '1小时前',
likes: 8,
comments: 2,
user: '装备控',
avatar: '👕',
liked: false
}
]);
const [inputText, setInputText] = useState('');
const flatListRef = useRef<FlatList>(null);
const handleSend = () => {
if (inputText.trim() === '') {
Alert.alert('提示', '请输入留言内容');
return;
}
const newMessage: Message = {
id: Date.now().toString(),
text: inputText,
timestamp: '刚刚',
likes: 0,
comments: 0,
user: '我',
avatar: '👤',
liked: false
};
setMessages([newMessage, ...messages]);
setInputText('');
// 滚动到顶部
setTimeout(() => {
flatListRef.current?.scrollToIndex({ index: 0, animated: true });
}, 100);
};
const handleLike = (id: string) => {
setMessages(messages.map(msg =>
msg.id === id
? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
: msg
));
};
const handleDelete = (id: string) => {
Alert.alert(
'确认删除',
'确定要删除这条留言吗?',
[
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: () => setMessages(messages.filter(msg => msg.id !== id))
}
]
);
};
const handleEdit = (id: string) => {
const messageToEdit = messages.find(msg => msg.id === id);
if (messageToEdit) {
setInputText(messageToEdit.text);
setMessages(messages.filter(msg => msg.id !== id));
}
};
const handleReply = (id: string) => {
Alert.alert('回复', `回复给 ${messages.find(m => m.id === id)?.user}`);
};
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>动态留言板</Text>
<TouchableOpacity style={styles.infoButton} onPress={() => Alert.alert('帮助', '这是一个动态留言列表,您可以发表、点赞、评论和管理留言')}>
<Text style={styles.infoText}>ℹ️</Text>
</TouchableOpacity>
</View>
{/* 输入区域 */}
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
value={inputText}
onChangeText={setInputText}
placeholder="写下您的动态..."
multiline
numberOfLines={3}
/>
<TouchableOpacity
style={[styles.sendButton, inputText.trim() === '' && styles.sendButtonDisabled]}
onPress={handleSend}
disabled={inputText.trim() === ''}
>
<Text style={styles.sendButtonText}>{ICONS.send} 发布</Text>
</TouchableOpacity>
</View>
{/* 留言列表 */}
<FlatList
ref={flatListRef}
data={messages}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<MessageItem
message={item}
onLike={handleLike}
onDelete={handleDelete}
onEdit={handleEdit}
onReply={handleReply}
/>
)}
style={styles.listContainer}
showsVerticalScrollIndicator={false}
ListHeaderComponent={
<Text style={styles.sectionTitle}>最新动态 ({messages.length})</Text>
}
/>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.message}</Text>
<Text style={styles.navText}>动态</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.like}</Text>
<Text style={styles.navText}>点赞</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>{ICONS.comment}</Text>
<Text style={styles.navText}>评论</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.menu}</Text>
<Text style={styles.navText}>我的</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8fafc',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
infoButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#f1f5f9',
alignItems: 'center',
justifyContent: 'center',
},
infoText: {
fontSize: 18,
color: '#64748b',
},
inputContainer: {
backgroundColor: '#ffffff',
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
textInput: {
borderWidth: 1,
borderColor: '#cbd5e1',
borderRadius: 8,
padding: 12,
fontSize: 16,
color: '#1e293b',
height: 80,
textAlignVertical: 'top',
backgroundColor: '#f8fafc',
},
sendButton: {
backgroundColor: '#3b82f6',
padding: 12,
borderRadius: 8,
alignItems: 'center',
marginTop: 12,
},
sendButtonDisabled: {
backgroundColor: '#9ca3af',
},
sendButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '500',
},
listContainer: {
flex: 1,
padding: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 16,
},
messageItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
messageHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
userAvatar: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#dbeafe',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
avatarText: {
fontSize: 20,
},
userInfo: {
flex: 1,
},
userName: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
timestamp: {
fontSize: 12,
color: '#64748b',
marginTop: 2,
},
menuButton: {
padding: 4,
},
menuText: {
fontSize: 20,
color: '#94a3b8',
},
messageContent: {
marginBottom: 12,
},
messageText: {
fontSize: 16,
color: '#334155',
lineHeight: 22,
},
expandText: {
color: '#3b82f6',
fontSize: 14,
marginTop: 8,
},
messageActions: {
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingTop: 12,
},
actionButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
actionIcon: {
fontSize: 18,
marginRight: 6,
color: '#64748b',
},
actionText: {
fontSize: 14,
color: '#64748b',
},
liked: {
color: '#ef4444',
},
likedText: {
color: '#ef4444',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingBottom: 2,
borderBottomWidth: 2,
borderBottomColor: '#3b82f6',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#3b82f6',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#3b82f6',
fontWeight: '500',
},
});
export default MessageBoardDynamicListApp;
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)