在React Native中开发一个运动类型管理系统,你需要考虑几个关键部分:用户界面设计、数据管理、以及可能的后端交互。下面是一个基本的步骤指南和代码示例,帮助你开始构建这样一个应用。
- 初始化项目
首先,确保你已经安装了React Native环境。如果没有,你可以通过以下命令来安装:
bash
npx react-native init MotionTypeManager
cd MotionTypeManager
- 安装依赖
你可能需要一些额外的库来帮助你管理状态或者进行数据交互,例如react-navigation用于导航,react-native-paper用于UI组件等。
bash
npm install @react-navigation/native @react-navigation/stack react-native-paper
npm install @react-navigation/native-stack 如果使用v6+的React Navigation
- 设置导航
使用React Navigation设置应用的导航结构。例如,你可以创建一个堆栈导航器,其中包括运动类型列表和运动类型详情页面。
javascript
// 在App.js中设置
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
- 创建运动类型列表页面(HomeScreen)
javascript
// screens/HomeScreen.js
import * as React from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { List } from 'react-native-paper';
const exerciseTypes = [
{ id: '1', name: '跑步' },
{ id: '2', name: '游泳' },
{ id: '3', name: '瑜伽' },
// 更多运动类型...
];
const HomeScreen = ({ navigation }) => (
<View>
<FlatList
data={exerciseTypes}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => navigation.navigate('Details', { item })}>
<List.Item title={item.name} />
</TouchableOpacity>
)}
/>
</View>
);
export default HomeScreen;
- 创建运动类型详情页面(DetailsScreen)
javascript
// screens/DetailsScreen.js
import * as React from 'react';
import { View, Text } from 'react-native';
import { Card } from 'react-native-paper';
const DetailsScreen = ({ route }) => {
const { item } = route.params; // 从路由参数中获取运动类型数据
return (
<View>
<Card>
<Card.Title title={item.name} />
<Card.Content>
<Text>这里是关于{item.name}的详细信息。</Text>
</Card.Content>
</Card>
</View>
);
};
export default DetailsScreen;
- 运行应用
使用以下命令运行你的React Native应用:
bash
npx react-native run-Harmony 或者 npx react-native run-Harmony,取决于你的平台选择。 确保你的模拟器或设备已经准备好。 如果是真实设备,请使用相应的连接命令。 例如:adb devices 查看设备是否连接成功。然后使用 npx react-native run-Harmony --deviceId <你的设备ID>。 如果你用的是Harmony设备,请确保Xcode正确配置。 你可以用npx react-native run-Harmony来运行。 如果是真机调试,确保USB调试模式开启,并选择正确的设备。 如果是模拟器,确保模拟器已启动并选择正确的模拟器。 如果是Expo管理的项目,可以使用expo start然后扫描二维码或在浏览器中打开URL来运行应用。 对于裸React Native项目,你需要使用相应的命令行工具来运行。 对于Harmony,通常是npx react-native run
真实项目案例演示参考:
js
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput, Modal, Alert } from 'react-native';
// Base64 图标库
const ICONS = {
running: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMy40OSAxMy40OUwxMiAxNWwtMS40OS0xLjQ5TDEyIDEybDEuNDkgMS40OXpNMTguODYgMTMuNzFMMTQgMTguNTdsLTEuMjktMS4yOUMxNi4yMyAxNC40NSAxOCAxMC40MyAxOCA2YzAtMy4zMS0yLjY5LTYtNi02UzYgMi42OSA2IDZjMCA0LjQzIDEuNzcgOC40NSA1LjI5IDExLjI4TDEwIDE4LjU3bC00Ljg2LTQuODZMMTggMi44NWwxLjQyIDEuNDJMMTQuNTcgMTJsMS40MiAxLjQybDEuNDItMS40MnoiLz48L3N2Zz4=',
cycling: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMy41IDUuNUwxMiA3bC0xLjUtMS41TDkgOS41bDEuNSAxLjVMOSAyMWgtMnYtNi41bC0yLjUtMi41TDIgMTVsMi41IDIuNUw3IDE1djZINXY0aDR2LTRoMnY0aDR2LTRoLTJsMi41LTIuNUwyMiAxNWwtMi41LTIuNUwxNyA5LjVsLTEuNSAxLjVMOSA5bDEuNS0xLjVMMTIgNmwxLjUgMS41eiIvPjwvc3ZnPg==',
swimming: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0yMS44OCAxMC41NkMyMC4wNiA0LjQyIDE0LjIzIDAgMTIgMEM5Ljc3IDAgMy45NCA0LjQyIDIuMTIgMTAuNTZDMi4wNiAxMC43NSAyIDEwLjk1IDIgMTFINmMtLjAyLS4xNy0uMDItLjM0LS4wMi0uNTFDMTYuMDMgMTAuMjMgMjAgNi4yMSAyMCAxMmMwIC4xOS0uMDIuMzgtLjA0LjU3SDIyYy4wMi0uMTkuMDItLjM5LjAyLS41N3ptLTE5Ljg4Ljg0QzMuNzQgNS4yNiA4LjY3IDEgMTIgMWMzLjMyIDAgOC4yNSA0LjI2IDkuOTggMTAuMTRDNy45NCAxMS4yNCAzLjkyIDE1LjI2IDMuOTIgMjBjMCAuMTkuMDIuMzguMDQuNTdINmMuMDItLjE5LjAyLS4zOC4wMi0uNTdDNi4wMiAxNS43OSAxMCAxMS43NyAxMCAxMmMwIC4xOS0uMDIuMzgtLjA0LjU3SDE4Yy4wMi0uMTkuMDItLjM5LjAyLS41N2MtMS43NCA2LjE0LTYuNjcgMTAuNC05Ljk4IDEwLjRjLTMuMjMgMC04LjE2LTQuMjYtOS45LTExLjQzem05Ljk4IDEuNTRjLTIuMjEgMC00IDEuNzktNCA0czEuNzkgNCA0IDQtNCAxLjc5LTQgNC4wMS0xLjc5IDQtNCA0IDEuNzkgNCA0IDRjMi4yMSAwIDQtMS43OSA0LTRzLTEuNzktNC00LTR6bTAgNi40NWMtMS4zMyAwLTIuNDItMS4wOS0yLjQyLTIuNDJTNy42NyAxNS41OCA5IDE1LjU4czIuNDIgMS4wOSAyLjQyIDIuNDJTNy42NyAyMiA2LjM0IDIyeiIvPjwvc3ZnPg==',
yoga: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAzbC0xLjUgMS41TDEyIDZsMS41LTEuNUwxMiAzem03LjUgNGwtMS41IDEuNUwxOS41IDdsMS41LTEuNUwxOS41IDd6TTQuNSA3TDMgOC41bDEuNSAxLjVMNiA4LjVsLTEuNS0xLjVMNiA4LjV6TTIxIDEybC0xLjUgMS41TDIxIDE1bDEuNS0xLjVMIDIxIDEyem0tMTggMGwtMS41IDEuNUwzIDE1bDEuNS0xLjVMMyAxMnptMTUuNSA0LjVsLTEuNSAxLjVMMTUuNSAyMWwxLjUtMS41TDE4LjUgMTh6TTQuNSAxOC41TDMgMjBsMS41IDEuNUw2IDE4LjVsLTEuNS0xLjVMNiAxOC41eiIvPjxwYXRoIGQ9Ik0xMiA4Yy0yLjIxIDAtNCAxLjc5LTQgNHMxLjc5IDQgNCA0IDQtMS43OSA0LTQtMS43OS00LTQtNHptMCA2Yy0xLjExIDAtMi0uODktMi0ycy44OS0yIDItMiAyIC44OSAyIDItLjg5IDItMiAyeiIvPjxwYXRoIGQ9Ik0xMiAxNmMtMi43NiAwLTUgMi4yNC01IDVzMi4yNCA1IDUgNSA1LTIuMjQgNS01LTIuMjQtNS01LTV6bTAgOGMtMS42NiAwLTMtMS4zNC0zLTNzMS4zNC0zIDMtMyAzIDEuMzQgMyAzLTEuMzQgMy0zIDN6Ii8+PC9zdmc+',
basketball: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
football: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
tennis: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
plus: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSAxM2gtNnY2aC0ydi02SDV2LTJoNlY1aDJ2Nmg2djJ6Ii8+PC9zdmc+',
edit: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0zIDE3LjI1VjIxaDMuNzVMMTcuODEgOS45NGwtMy43NS0zLjc1TDMgMTcuMjV6TTIwLjcxIDcuMDRjLjM5LS4zOS4zOS0xLjAyIDAtMS40MUwxOC4zNy4yOGMtLjM5LS4zOS0xLjAyLS4zOS0xLjQxIDBMMTUuMTMgMi4xMWwzLjc1IDMuNzVMMjAuNzEgNy4wNHoiLz48L3N2Zz4=',
delete: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik02IDE5YzAgMS4xLjkgMiAyIDJoOGMxLjEgMCAyLS45IDItMlY3SDZ2MTJ6bTIuNDYtNy4xMmwxLjQxLTEuNDFMNy4wNSAxMi4zbDEuNDIgMS40MUwxMCAxMC44M2wxLjQxIDEuNDEgMS40Mi0xLjQxLTEuNDEtMS40MUwxMy44MyAxMmwuMDEtLjAxLTEuNDEtMS40MUwxMCA3LjE3bC0xLjQxIDEuNDFMOC41OSAxMmwuMDEuMDF6TTcgMkgxNkMxNy4xIDItMTggMi45LTE4IDR2MUgxNlY0SDh2MTJIMlY0YzAtMS4xLjktMiAyLTJ6Ii8+PC9zdmc+',
close: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+PC9zdmc+'
};
// 默认运动类型数据
const DEFAULT_SPORTS = [
{ id: '1', name: '跑步', icon: ICONS.running, color: '#FF6B6B' },
{ id: '2', name: '骑行', icon: ICONS.cycling, color: '#4ECDC4' },
{ id: '3', name: '游泳', icon: ICONS.swimming, color: '#45B7D1' },
{ id: '4', name: '瑜伽', icon: ICONS.yoga, color: '#96CEB4' },
{ id: '5', name: '篮球', icon: ICONS.basketball, color: '#FFEAA7' },
{ id: '6', name: '足球', icon: ICONS.football, color: '#DDA0DD' },
];
const SportManager: React.FC = () => {
const [sports, setSports] = useState(DEFAULT_SPORTS);
const [modalVisible, setModalVisible] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [currentSport, setCurrentSport] = useState({ id: '', name: '', color: '#4ECDC4' });
const [newSportName, setNewSportName] = useState('');
// 打开添加运动类型模态框
const openAddModal = () => {
setIsEditing(false);
setCurrentSport({ id: '', name: '', color: '#4ECDC4' });
setNewSportName('');
setModalVisible(true);
};
// 打开编辑运动类型模态框
const openEditModal = (sport: any) => {
setIsEditing(true);
setCurrentSport(sport);
setNewSportName(sport.name);
setModalVisible(true);
};
// 保存运动类型
const saveSport = () => {
if (!newSportName.trim()) {
Alert.alert('错误', '请输入运动类型名称');
return;
}
if (isEditing) {
// 编辑现有运动类型
setSports(sports.map(sport =>
sport.id === currentSport.id
? { ...sport, name: newSportName }
: sport
));
} else {
// 添加新运动类型
const newSport = {
id: Date.now().toString(),
name: newSportName,
icon: ICONS.running, // 默认图标
color: currentSport.color
};
setSports([...sports, newSport]);
}
setModalVisible(false);
};
// 删除运动类型
const deleteSport = (id: string) => {
Alert.alert(
'确认删除',
'确定要删除这个运动类型吗?',
[
{ text: '取消', style: 'cancel' },
{ text: '删除', style: 'destructive', onPress: () => setSports(sports.filter(sport => sport.id !== id)) }
]
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>运动类型管理</Text>
<Text style={styles.subtitle}>管理您的运动项目分类</Text>
</View>
<ScrollView contentContainerStyle={styles.content}>
<View style={styles.sportGrid}>
{sports.map((sport) => (
<View key={sport.id} style={[styles.sportCard, { backgroundColor: sport.color + '20' }]}>
<View style={[styles.iconContainer, { backgroundColor: sport.color }]}>
<Text style={styles.icon}>{decodeURIComponent(escape(atob(sport.icon.split(',')[1])))}</Text>
</View>
<Text style={[styles.sportName, { color: sport.color }]}>{sport.name}</Text>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.actionButton, { backgroundColor: sport.color }]}
onPress={() => openEditModal(sport)}
>
<Text style={styles.buttonText}>编辑</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, { backgroundColor: '#FF6B6B' }]}
onPress={() => deleteSport(sport.id)}
>
<Text style={styles.buttonText}>删除</Text>
</TouchableOpacity>
</View>
</View>
))}
<TouchableOpacity style={styles.addCard} onPress={openAddModal}>
<View style={styles.addIconContainer}>
<Text style={styles.addIcon}>+</Text>
</View>
<Text style={styles.addText}>添加新运动</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* 添加/编辑运动类型模态框 */}
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<Text style={styles.modalTitle}>{isEditing ? '编辑运动类型' : '添加新运动类型'}</Text>
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.closeButton}>×</Text>
</TouchableOpacity>
</View>
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>运动名称</Text>
<TextInput
style={styles.input}
value={newSportName}
onChangeText={setNewSportName}
placeholder="请输入运动名称"
/>
</View>
<View style={styles.colorPickerContainer}>
<Text style={styles.inputLabel}>选择颜色</Text>
<View style={styles.colorOptions}>
{['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD'].map((color) => (
<TouchableOpacity
key={color}
style={[
styles.colorOption,
{ backgroundColor: color },
currentSport.color === color && styles.selectedColor
]}
onPress={() => setCurrentSport({ ...currentSport, color })}
/>
))}
</View>
</View>
<TouchableOpacity style={styles.saveButton} onPress={saveSport}>
<Text style={styles.saveButtonText}>保存</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
paddingTop: 30,
paddingBottom: 20,
paddingHorizontal: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e9ecef',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#212529',
textAlign: 'center',
},
subtitle: {
fontSize: 14,
color: '#6c757d',
textAlign: 'center',
marginTop: 4,
},
content: {
padding: 16,
},
sportGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
sportCard: {
width: '48%',
backgroundColor: '#ffffff',
borderRadius: 16,
padding: 20,
marginBottom: 16,
alignItems: 'center',
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
iconContainer: {
width: 60,
height: 60,
borderRadius: 30,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 15,
},
icon: {
fontSize: 28,
color: '#ffffff',
},
sportName: {
fontSize: 18,
fontWeight: '600',
marginBottom: 20,
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
},
actionButton: {
flex: 1,
paddingVertical: 8,
paddingHorizontal: 12,
borderRadius: 8,
marginHorizontal: 4,
alignItems: 'center',
},
buttonText: {
color: '#ffffff',
fontWeight: '600',
fontSize: 14,
},
addCard: {
width: '48%',
backgroundColor: '#ffffff',
borderRadius: 16,
padding: 20,
marginBottom: 16,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 2,
borderStyle: 'dashed',
borderColor: '#ced4da',
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
},
addIconContainer: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#e9ecef',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 15,
},
addIcon: {
fontSize: 32,
color: '#6c757d',
},
addText: {
fontSize: 16,
color: '#6c757d',
fontWeight: '600',
},
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
backgroundColor: '#ffffff',
width: '85%',
borderRadius: 20,
padding: 25,
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
modalTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#212529',
},
closeButton: {
fontSize: 30,
color: '#adb5bd',
fontWeight: '200',
},
inputContainer: {
marginBottom: 20,
},
inputLabel: {
fontSize: 16,
fontWeight: '600',
color: '#495057',
marginBottom: 10,
},
input: {
borderWidth: 1,
borderColor: '#ced4da',
borderRadius: 12,
paddingHorizontal: 15,
paddingVertical: 12,
fontSize: 16,
backgroundColor: '#f8f9fa',
},
colorPickerContainer: {
marginBottom: 25,
},
colorOptions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
colorOption: {
width: 40,
height: 40,
borderRadius: 20,
},
selectedColor: {
borderWidth: 3,
borderColor: '#ffffff',
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
},
saveButton: {
backgroundColor: '#4361ee',
paddingVertical: 15,
borderRadius: 12,
alignItems: 'center',
},
saveButtonText: {
fontSize: 18,
fontWeight: 'bold',
color: '#ffffff',
},
});
export default SportManager;
这段React Native代码实现了一个运动类型管理系统,通过状态管理和组件化设计构建了完整的CRUD功能。系统核心基于useState Hook维护运动数据状态,通过模态框实现添加和编辑操作,利用Alert组件确认删除操作,体现了响应式编程的思想。
从鸿蒙系统角度分析,该系统展现了跨平台开发的核心优势。鸿蒙的分布式架构允许此类应用无缝运行在手机、平板、智能手表等多种设备上。运动类型数据可通过鸿蒙的分布式数据服务实现多设备同步,用户在手机上添加的运动类型可自动出现在平板设备上。鸿蒙的Ability框架支持将运动管理功能封装为独立服务能力,通过Intent机制实现应用间数据传递,构建运动健康生态。
代码中的状态管理模式与鸿蒙的数据驱动理念高度契合。useState Hook维护的sports状态数组相当于鸿蒙应用中的数据模型,每次状态更新都会触发UI重新渲染,这与鸿蒙的响应式数据绑定机制相似。在鸿蒙原生开发中,可通过@State装饰器实现类似效果,当运动类型数据发生变化时,相关UI组件会自动更新。
运动类型卡片的设计体现了鸿蒙的设计语言。每个卡片通过color属性动态设置背景色和文字色,这与鸿蒙的动态主题色系统相呼应。鸿蒙系统支持根据用户壁纸自动生成主题色,此类动态配色方案可提升应用的视觉一致性。卡片布局采用网格排列,适配不同屏幕尺寸,符合鸿蒙的响应式布局原则。
模态框的实现方式在鸿蒙生态中有独特优势。React Native的Modal组件通过透明遮罩和滑动动画提供流畅体验,这与鸿蒙的ArkUI框架中的弹窗组件设计理念一致。鸿蒙系统支持更丰富的交互动画,如弹性动画、路径动画等,可为运动类型管理操作提供更生动的反馈效果。
数据持久化方面,代码可通过集成AsyncStorage实现本地存储,这在鸿蒙生态中可替换为HarmonyOS的轻量级数据存储Preference。对于需要云端同步的数据,可接入鸿蒙的云开发服务AppGallery Connect,实现数据的远程存储和同步。鸿蒙的统一数据管理服务可为运动类型数据提供标准化的数据模型,便于与其他健康应用进行数据交换。
图标处理方式展现了跨平台开发的复杂性。代码中通过base64编码处理图标数据,这在鸿蒙系统中可通过资源管理框架优化。鸿蒙支持多种资源类型,包括图片、字符串、颜色等,可通过资源ID直接引用,减少应用包大小。系统级的图标库可为运动类型提供统一的视觉规范,提升用户体验。
颜色管理机制体现了现代UI设计趋势。代码中为每个运动类型设置特定颜色,这与鸿蒙的Material You设计语言相契合。系统可根据用户偏好动态调整界面色彩,运动类型卡片的颜色可与系统主题色动态匹配,实现个性化界面。鸿蒙的色彩系统支持浅色和深色模式自动切换,确保在不同环境下都有良好的可视性。
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

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

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