React Native鸿蒙跨平台组件通信的核心模式——props 传递和回调函数——在 ArkUI 中同样存在对应实现(@Prop 和自定义事件)
数据语义与状态域设计
- PetMessage 和 StatusStat 用类型把消息生命周期与统计维度固化为“协议层”。视图只消费这些纯 JS 对象,避免平台特有字段,Bridge 传输的是稳定的结构化数据,在鸿蒙 ArkUI、iOS UIKit、Android View 三端都具备一致可预期的表现。
- 可选属性 readTime、deliveryTime 用联合类型表现状态的附加语义,MessageItem 渲染时以条件分支决定显示,保证 UI 与状态域一一对应,不需要平台分支或原生 API。
状态映射的正确性与跨端一致性
- MessageItem 通过 getStatusIcon/getStatusColor/getStatusText 将业务状态映射到交互语义,属于“纯 JS 决策→UI 展现”的路径,Bridge 仅接收最终的文本与颜色值。
- StatusStatCard 的图标选择以英文枚举 sent/delivered/read 为依据,但 stats 数据用中文“已发送/已送达/已读/失败”。这会让所有卡片都命中默认分支并显示错误图标。跨端修复的关键是“输入规范化”,将中文状态在 JS 层规范到统一枚举,避免平台端或桥接端产生分歧。
const normalizeStatus = (s: string) =>
({ 已发送: 'sent', 已送达: 'delivered', 已读: 'read', 失败: 'error' } as const)[s] ?? s;
const StatusStatCard = ({ stat }: { stat: StatusStat }) => {
const code = normalizeStatus(stat.status);
const icon = code === 'sent' ? ICONS.sent : code === 'delivered' ? ICONS.delivered : code === 'read' ? ICONS.read : ICONS.error;
return (
<View>
<Text>{icon}</Text>
<Text>{stat.count}</Text>
</View>
);
};
列表虚拟化与滚动容器的职责划分
- 消息列表用 FlatList 渲染并以 id 作为稳定 key,这是跨端可伸缩的基线配置。滚动与窗口化由原生后端负责,减少 JS→UI 往返。
- 外层以 ScrollView 包裹统计区、趋势图和 FlatList,会造成“嵌套滚动”的事件竞争与重复布局。跨端最佳实践是让 FlatList 接管全页滚动,将统计区、趋势图作为 ListHeaderComponent 注入;性能更稳定,ArkUI 的合成层也更容易优化。
const Header = (
<View>
<View><Text>宠物消息回执系统</Text></View>
<View>{stats.map((s, i) => <StatusStatCard key={i} stat={s} />)}</View>
<ActivityTrendChart />
<View><Text>消息列表</Text><Text>({messages.length} 条消息)</Text></View>
</View>
);
<FlatList
data={messages}
keyExtractor={item => item.id}
renderItem={({ item }) => <MessageItem message={item} />}
ListHeaderComponent={Header}
removeClippedSubviews
initialNumToRender={8}
windowSize={5}
/>
图形渲染路径:布局驱动与原生绘制
- 活跃度趋势以 View 组合柱形图并用百分比 height 表达柱高,零依赖且语义清晰,但每次更新都触发布局与测量,JS→UI 同步链较长。
- 更稳健的跨端选择是矢量绘制(react-native-svg 或 Skia)。柱形的几何参数一次下发到原生绘制后端,合成层更新不依赖布局重算;若需要动效,配合原生驱动的 transform/opacity 可获得三端一致的帧表现。
import Svg, { Rect, Text as SvgText } from 'react-native-svg';
const Bars = ({ data, w = 320, h = 100, color = '#10b981' }) => {
const max = Math.max(...data.map(d => d.level), 1);
const bw = w / data.length;
return (
<Svg width={w} height={h}>
{data.map((d, i) => {
const bh = (d.level / max) * (h - 20);
const y = h - bh;
return (
<>
<Rect key={`bar-${i}`} x={i * bw + bw * 0.2} y={y} width={bw * 0.6} height={bh} rx={3} fill={color} />
<SvgText key={`lab-${i}`} x={i * bw + bw * 0.5} y={h - 4} fontSize="10" textAnchor="middle">{d.day}</SvgText>
</>
);
})}
</Svg>
);
};
进度条与活动值:原生驱动替代布局重算
- 活跃度条当前用 width 百分比表达填充量,更新会触发布局。跨端性能更优的模式是以 transform 的 scaleX 或位移驱动,开启 useNativeDriver,将插值放到 UI 线程,避免 JS 定时器与布局重算的抖动。
import { Animated, useEffect, useRef } from 'react';
const ActivityBar = ({ level }: { level: number }) => {
const v = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(v, { toValue: level / 100, duration: 250, useNativeDriver: true }).start();
}, [level]);
return (
<View style={{ width: 100, height: 6, overflow: 'hidden' }}>
<Animated.View style={{ width: 100, height: 6, backgroundColor: '#10b981', transform: [{ scaleX: v }] }} />
</View>
);
};
交互桥接与业务动作
- Alert 在三端映射为原生弹窗,闭包回调把用户选择带回 JS 线程。refreshMessages 与 retryFailedMessage 都是“轻交互”示例,真实场景建议把多条更新合并为一次 setState 或 reducer dispatch,降低 Bridge 消息频率。
- 错误重试逻辑未与 UI绑定,MessageItem 可在 error 状态下暴露重试入口,并通过 id 定位数据项,保持行为的单一来源。
const MessageItem = ({ message, onRetry }: { message: PetMessage; onRetry?: (id: string) => void }) => (
<View>
{message.status === 'error' && (
<TouchableOpacity onPress={() => onRetry?.(message.id)}>
<Text>重试</Text>
</TouchableOpacity>
)}
</View>
);
渲染稳定性与重绘控制
- 为消息项使用 React.memo 并将 renderItem 通过 useCallback 固定,避免父级状态细微变化触发整列表重绘;鸿蒙 ArkUI 下也能减少视图树重排。
- 对近似等高的消息项提供 getItemLayout,让三端滚动定位更确定;配合 removeClippedSubviews 提升长列表的内存与合成表现。
const ITEM_HEIGHT = 140;
const getItemLayout = (_: PetMessage[], index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
图标策略与可访问性
- Emoji 图标零依赖且跨端可用,但不同平台字体渲染差异明显。需要统一外观与颜色控制时,建议切换到 react-native-svg 或图标字库,确保 ArkUI、Skia、CoreGraphics 渲染一致。
- 状态图标与文案都源自同一状态域,避免在不同组件中分散判断造成端间表现不一致。
在跨平台移动应用开发领域,React Native 凭借其独特的架构设计理念,将声明式编程范式与原生渲染能力相结合,为开发者提供了一条兼顾开发效率与应用性能的路径。宠物消息回执系统这一应用虽然功能场景较为独特,但其技术实现中蕴含的设计模式和工程实践值得深入探讨。
类型驱动的数据建模策略
该应用采用了 TypeScript 进行全面的类型建模,这不仅是代码规范的要求,更是跨平台开发中确保数据一致性的重要手段。消息状态、统计信息、活跃度数据等核心业务实体都经过了严格的类型定义:
type PetMessage = {
id: string;
petName: string;
content: string;
timestamp: string;
status: 'sent' | 'delivered' | 'read' | 'error';
readTime?: string;
deliveryTime?: string;
activityLevel: number;
};
类型定义中的联合类型(status: 'sent' | 'delivered' | 'read' | 'error')体现了领域建模的精确性。这种模式在跨平台开发中尤为重要——当同一个业务实体需要在不同平台的原生模块之间传递时,类型系统可以作为隐式的契约文档,确保数据结构的跨平台一致性。鸿蒙端的 ArkTS 虽然有自己独立的类型系统,但通过 TypeScript 定义共享的类型接口,可以有效避免因类型理解差异导致的运行时错误。
状态可视化的实现哲学
应用中最具技术含量的部分在于消息状态的动态展示。状态图标和颜色并非简单的静态映射,而是根据消息的实时状态进行条件渲染:
const getStatusColor = () => {
switch (message.status) {
case 'sent': return '#f59e0b';
case 'delivered': return '#3b82f6';
case 'read': return '#10b981';
case 'error': return '#ef4444';
default: return '#6b7280';
}
};
这种状态驱动的渲染模式体现了 React 声明式编程的核心思想——开发者只需描述状态与UI的映射关系,框架负责处理状态变化时的DOM更新。在鸿蒙平台上,虽然 UI 渲染机制与 React Native 不同(鸿蒙使用 ArkUI 的声明式语法),但这种"状态驱动视图"的理念是相通的。理解这一点对于掌握跨平台开发的设计精髓至关重要。
统计数据的聚合展示
状态统计卡片组件展示了如何在 UI 层处理聚合数据:
const [stats] = useState<StatusStat[]>([
{ status: '已发送', count: 12, percentage: 75, color: '#f59e0b' },
{ status: '已送达', count: 10, percentage: 62.5, color: '#3b82f6' },
{ status: '已读', count: 8, percentage: 50, color: '#10b981' },
{ status: '失败', count: 2, percentage: 12.5, color: '#ef4444' },
]);
百分比字段的引入是一个值得关注的设计决策。这意味着数据聚合逻辑在到达组件之前已经完成计算,而非在渲染阶段实时计算。这种"预处理"策略在跨平台开发中具有重要的性能意义——不同平台的计算能力存在差异,将计算密集型操作前置到数据层可以确保各平台渲染性能的一致性。
趋势图表的自定义实现
应用中没有使用第三方图表库,而是通过基础组件构建了一个轻量级的柱状图:
const ActivityTrendChart = () => {
const data = [
{ day: '周一', level: 75 },
{ day: '周二', level: 82 },
// ...
];
const maxValue = 100;
return (
<View style={styles.chartContainer}>
<View style={styles.chartBar}>
<View
style={[
styles.chartFill,
{
height: `${(item.level / maxValue) * 100}%`,
backgroundColor: item.level > 80 ? '#10b981' : item.level > 60 ? '#f59e0b' : '#ef4444'
}
]}
/>
</View>
</View>
);
};
这种自定义实现方式体现了 React Native 的设计哲学——提供足够灵活的基础组件,让开发者根据业务需求构建所需的 UI 元素。相较于引入沉重的图表库,这种轻量级方案在应用中更为实用。在向鸿蒙平台迁移时,这种基于基础组件的实现方式也意味着迁移工作量的可控性——只需确保基础组件的跨平台兼容性即可。
消息列表的高效渲染
FlatList 组件的使用展示了 React Native 处理长列表的标准范式:
<FlatList
data={messages}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<MessageItem message={item} />
)}
showsVerticalScrollIndicator={false}
/>
FlatList 的虚拟化机制是 React Native 性能优化的关键。通过只渲染可视区域内的元素,FlatList 能够在数据量较大时保持流畅的滚动体验。这一机制在跨平台场景下的重要性更加突出——不同平台的内存和渲染资源存在差异,虚拟化列表可以有效避免因资源耗尽导致的崩溃问题。鸿蒙平台虽然有自己的一套列表组件实现,但 React Native 的虚拟化思想在ArkUI中同样适用。
组件通信的模式演进
子组件通过回调函数与父组件通信的设计体现了单向数据流的原则:
const MessageItem = ({ message }: { message: PetMessage }) => {
// 组件内部逻辑
return (
<View style={styles.messageItem}>
{/* UI 渲染 */}
</View>
);
};
这种"受控组件"模式在跨平台开发中具有显著优势。当应用需要适配多个平台时,清晰的父子组件边界使得平台特定代码的注入更加可控。鸿蒙端的实现虽然语法不同,但组件通信的核心模式——props 传递和回调函数——在 ArkUI 中同样存在对应实现(@Prop 和自定义事件)。
真实演示案例代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';
// 图标库
const ICONS = {
pet: '🐶',
message: '💬',
sent: '📤',
delivered: '📥',
read: '👁️',
error: '❌',
activity: '⚡',
stats: '📊',
};
const { width } = Dimensions.get('window');
// 消息类型
type PetMessage = {
id: string;
petName: string;
content: string;
timestamp: string;
status: 'sent' | 'delivered' | 'read' | 'error';
readTime?: string;
deliveryTime?: string;
activityLevel: number; // 活跃度
};
// 状态统计类型
type StatusStat = {
status: string;
count: number;
percentage: number;
color: string;
};
// 消息项组件
const MessageItem = ({ message }: { message: PetMessage }) => {
const getStatusIcon = () => {
switch (message.status) {
case 'sent': return ICONS.sent;
case 'delivered': return ICONS.delivered;
case 'read': return ICONS.read;
case 'error': return ICONS.error;
default: return ICONS.message;
}
};
const getStatusColor = () => {
switch (message.status) {
case 'sent': return '#f59e0b';
case 'delivered': return '#3b82f6';
case 'read': return '#10b981';
case 'error': return '#ef4444';
default: return '#6b7280';
}
};
const getStatusText = () => {
switch (message.status) {
case 'sent': return '已发送';
case 'delivered': return '已送达';
case 'read': return '已读';
case 'error': return '发送失败';
default: return '未知';
}
};
return (
<View style={styles.messageItem}>
<View style={styles.messageHeader}>
<View style={[styles.statusIcon, { backgroundColor: `${getStatusColor()}20` }]}>
<Text style={[styles.statusIconText, { color: getStatusColor() }]}>
{getStatusIcon()}
</Text>
</View>
<View style={styles.messageInfo}>
<Text style={styles.petName}>{message.petName}</Text>
<Text style={styles.timestamp}>{message.timestamp}</Text>
</View>
<Text style={[styles.statusText, { color: getStatusColor() }]}>
{getStatusText()}
</Text>
</View>
<Text style={styles.messageContent}>{message.content}</Text>
<View style={styles.messageFooter}>
<View style={styles.activityContainer}>
<Text style={styles.activityLabel}>活跃度</Text>
<View style={styles.activityBar}>
<View
style={[
styles.activityFill,
{
width: `${message.activityLevel}%`,
backgroundColor: message.activityLevel > 70 ? '#10b981' : message.activityLevel > 40 ? '#f59e0b' : '#ef4444'
}
]}
/>
</View>
<Text style={styles.activityValue}>{message.activityLevel}%</Text>
</View>
{message.readTime && (
<Text style={styles.readTime}>阅读时间: {message.readTime}</Text>
)}
</View>
</View>
);
};
// 状态统计卡片组件
const StatusStatCard = ({ stat }: { stat: StatusStat }) => {
return (
<View style={styles.statCard}>
<View style={[styles.statIcon, { backgroundColor: `${stat.color}20` }]}>
<Text style={[styles.statIconText, { color: stat.color }]}>
{stat.status === 'sent' ? ICONS.sent :
stat.status === 'delivered' ? ICONS.delivered :
stat.status === 'read' ? ICONS.read : ICONS.error}
</Text>
</View>
<View style={styles.statInfo}>
<Text style={styles.statValue}>{stat.count}</Text>
<Text style={styles.statLabel}>{stat.status}</Text>
<View style={styles.progressBar}>
<View
style={[
styles.progressFill,
{
width: `${stat.percentage}%`,
backgroundColor: stat.color
}
]}
/>
</View>
<Text style={styles.progressText}>{stat.percentage}%</Text>
</View>
</View>
);
};
// 活跃度趋势组件
const ActivityTrendChart = () => {
const data = [
{ day: '周一', level: 75 },
{ day: '周二', level: 82 },
{ day: '周三', level: 68 },
{ day: '周四', level: 90 },
{ day: '周五', level: 78 },
{ day: '周六', level: 85 },
{ day: '周日', level: 92 },
];
const maxValue = 100;
return (
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>宠物活跃度趋势</Text>
<View style={styles.chart}>
{data.map((item, index) => (
<View key={index} style={styles.chartItem}>
<Text style={styles.chartDay}>{item.day}</Text>
<View style={styles.chartBar}>
<View
style={[
styles.chartFill,
{
height: `${(item.level / maxValue) * 100}%`,
backgroundColor: item.level > 80 ? '#10b981' : item.level > 60 ? '#f59e0b' : '#ef4444'
}
]}
/>
</View>
<Text style={styles.chartValue}>{item.level}%</Text>
</View>
))}
</View>
</View>
);
};
// 主页面组件
const PetMessageReceiptSystemApp: React.FC = () => {
const [messages, setMessages] = useState<PetMessage[]>([
{
id: '1',
petName: '豆豆',
content: '主人,我在后院发现了一个新玩具,快来陪我玩!',
timestamp: '10:30',
status: 'read',
readTime: '10:32',
activityLevel: 85
},
{
id: '2',
petName: '小白',
content: '我饿了,什么时候开饭呀?',
timestamp: '11:15',
status: 'delivered',
deliveryTime: '11:16',
activityLevel: 72
},
{
id: '3',
petName: '小黑',
content: '今天散步走了好多路,好累呀...',
timestamp: '14:20',
status: 'sent',
activityLevel: 65
},
{
id: '4',
petName: '花花',
content: '汪汪!看见一只蝴蝶飞过去了!',
timestamp: '15:45',
status: 'read',
readTime: '15:47',
activityLevel: 90
},
{
id: '5',
petName: '球球',
content: '呜呜...我想你了,什么时候回家?',
timestamp: '16:30',
status: 'error',
activityLevel: 45
}
]);
const [stats] = useState<StatusStat[]>([
{ status: '已发送', count: 12, percentage: 75, color: '#f59e0b' },
{ status: '已送达', count: 10, percentage: 62.5, color: '#3b82f6' },
{ status: '已读', count: 8, percentage: 50, color: '#10b981' },
{ status: '失败', count: 2, percentage: 12.5, color: '#ef4444' },
]);
const refreshMessages = () => {
Alert.alert('刷新', '正在更新消息状态...');
};
const retryFailedMessage = (id: string) => {
Alert.alert('重试', `重试发送消息给 ${messages.find(m => m.id === id)?.petName}`);
};
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>宠物消息回执系统</Text>
<TouchableOpacity style={styles.refreshButton} onPress={refreshMessages}>
<Text style={styles.refreshText}>{ICONS.stats} 刷新</Text>
</TouchableOpacity>
</View>
{/* 统计卡片 */}
<ScrollView style={styles.content}>
<View style={styles.statsContainer}>
{stats.map((stat, index) => (
<StatusStatCard key={index} stat={stat} />
))}
</View>
{/* 活跃度趋势图表 */}
<ActivityTrendChart />
{/* 消息列表标题 */}
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>消息列表</Text>
<Text style={styles.messageCount}>({messages.length} 条消息)</Text>
</View>
{/* 消息列表 */}
<FlatList
data={messages}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<MessageItem message={item} />
)}
showsVerticalScrollIndicator={false}
/>
{/* 操作说明 */}
<View style={styles.instructionCard}>
<Text style={styles.instructionTitle}>使用说明</Text>
<Text style={styles.instructionText}>• 绿色表示消息已被宠物主人阅读</Text>
<Text style={styles.instructionText}>• 蓝色表示消息已送达宠物设备</Text>
<Text style={styles.instructionText}>• 黄色表示消息已发送</Text>
<Text style={styles.instructionText}>• 红色表示消息发送失败,可点击重试</Text>
</View>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.pet}</Text>
<Text style={styles.navText}>宠物</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.message}</Text>
<Text style={styles.navText}>消息</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>{ICONS.stats}</Text>
<Text style={styles.navText}>状态</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.activity}</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',
},
refreshButton: {
backgroundColor: '#3b82f6',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
refreshText: {
color: '#ffffff',
fontSize: 14,
fontWeight: '500',
},
content: {
flex: 1,
padding: 16,
},
statsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
marginBottom: 16,
},
statCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
width: (width - 48) / 2,
marginBottom: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
flexDirection: 'row',
alignItems: 'center',
},
statIcon: {
width: 36,
height: 36,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
statIconText: {
fontSize: 18,
},
statInfo: {
flex: 1,
},
statValue: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
statLabel: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
progressBar: {
width: '100%',
height: 6,
backgroundColor: '#e2e8f0',
borderRadius: 3,
marginTop: 8,
overflow: 'hidden',
},
progressFill: {
height: '100%',
borderRadius: 3,
},
progressText: {
fontSize: 10,
color: '#64748b',
marginTop: 4,
alignSelf: 'flex-end',
},
chartContainer: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
chartTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 12,
},
chart: {
flexDirection: 'row',
justifyContent: 'space-around',
},
chartItem: {
alignItems: 'center',
flex: 1,
marginHorizontal: 2,
},
chartDay: {
fontSize: 10,
color: '#64748b',
marginBottom: 8,
},
chartBar: {
width: 20,
height: 80,
backgroundColor: '#e2e8f0',
borderRadius: 4,
justifyContent: 'flex-end',
alignItems: 'center',
},
chartFill: {
width: '100%',
borderRadius: 4,
},
chartValue: {
fontSize: 10,
color: '#64748b',
marginTop: 4,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
},
messageCount: {
fontSize: 14,
color: '#64748b',
},
messageItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
messageHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
statusIcon: {
width: 32,
height: 32,
borderRadius: 16,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
statusIconText: {
fontSize: 16,
},
messageInfo: {
flex: 1,
},
petName: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
},
timestamp: {
fontSize: 12,
color: '#64748b',
},
statusText: {
fontSize: 12,
fontWeight: '500',
},
messageContent: {
fontSize: 14,
color: '#334155',
lineHeight: 20,
marginBottom: 12,
},
messageFooter: {
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingTop: 12,
},
activityContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 8,
},
activityLabel: {
fontSize: 12,
color: '#64748b',
width: 50,
},
activityBar: {
flex: 1,
height: 6,
backgroundColor: '#e2e8f0',
borderRadius: 3,
marginRight: 8,
overflow: 'hidden',
},
activityFill: {
height: '100%',
borderRadius: 3,
},
activityValue: {
fontSize: 10,
color: '#64748b',
width: 30,
},
readTime: {
fontSize: 12,
color: '#64748b',
},
instructionCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginTop: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
instructionTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
},
instructionText: {
fontSize: 12,
color: '#64748b',
lineHeight: 18,
marginBottom: 4,
},
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 PetMessageReceiptSystemApp;

打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

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

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

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


所有评论(0)