在React Native中实现鸿蒙跨平台开发中开发一个运动类型管理系统,使用React Navigation设置应用的导航结构,创建一个堆栈导航器

在React Native中开发一个运动类型管理系统,你需要考虑几个关键部分:用户界面设计、数据管理、以及可能的后端交互。下面是一个基本的步骤指南和代码示例,帮助你开始构建这样一个应用。

  1. 初始化项目

首先,确保你已经安装了React Native环境。如果没有,你可以通过以下命令来安装:

bash 复制代码
npx react-native init MotionTypeManager
cd MotionTypeManager
  1. 安装依赖

你可能需要一些额外的库来帮助你管理状态或者进行数据交互,例如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
  1. 设置导航

使用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;
  1. 创建运动类型列表页面(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;
  1. 创建运动类型详情页面(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;
  1. 运行应用

使用以下命令运行你的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工程目录去:

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

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

相关推荐
2301_796512528 小时前
使用状态管理、持久化存储或者利用现有的库来辅助React Native鸿蒙跨平台开发开发一个允许用户撤销删除的操作
javascript·react native·react.js
零Suger8 小时前
React 组件通信
前端·react.js·前端框架
hahjee8 小时前
diffutils文件对比:鸿蒙PC上的diff工具集
华为·harmonyos
2301_796512528 小时前
React Native鸿蒙跨平台开发包含输入收入金额、选择收入类别、记录备注和日期等功能,实战react-native-paper组件
javascript·react native·react.js
AI分享猿9 小时前
新手跨境电商实测:Apache 搭站,雷池 WAF 零基础部署
安全·web安全·react.js·网络安全·开源·apache
2401_860319529 小时前
react-native-calendarsReact Native库来帮助你处理日期和时间,实现鸿蒙跨平台开发日历组件
react native·react.js·harmonyos
赵财猫._.9 小时前
React Native鸿蒙开发实战(九):复杂业务场景实战与架构设计
react native·react.js·harmonyos
ifeng09189 小时前
uniapp开发鸿蒙:跨端兼容与条件编译实战
华为·uni-app·harmonyos