如何在React Native中实现鸿蒙跨平台开发任务列表页面在很多应用中都是一个常见的需求,比如待办事项列表、购物车列表等

在React Native中开发一个Harmony组件(通常指的是鸿蒙操作系统,但考虑到你可能指的是一般的组件或模块,我将以一般的组件开发为例进行说明,特别是针对任务列表页面的设计及实现。任务列表页面在很多应用中都是一个常见的需求,比如待办事项列表、购物车列表等。

  1. 环境准备

确保你已经安装了React Native环境,包括Node.js、npm(或yarn)、React Native CLI和相应的开发工具(如Harmony Studio或Xcode)。

  1. 创建新项目

如果你还没有创建项目,可以使用以下命令来创建一个新的React Native项目:

bash 复制代码
npx react-native init TaskListApp
cd TaskListApp
  1. 设计任务列表页面

3.1 布局设计

使用Flexbox或者Grid布局来设计你的任务列表页面。在React Native中,你可以使用ViewText组件来构建基本的UI元素,例如:

jsx 复制代码
import React from 'react';
import { View, Text, StyleSheet, FlatList, TouchableOpacity } from 'react-native';

const TaskItem = ({ task }) => (
  <TouchableOpacity style={styles.taskItem}>
    <Text style={styles.taskText}>{task.title}</Text>
  </TouchableOpacity>
);

const TaskList = ({ tasks }) => (
  <FlatList
    data={tasks}
    renderItem={({ item }) => <TaskItem task={item} />}
    keyExtractor={item => item.id.toString()}
  />
);

3.2 样式设计

使用StyleSheet来定义样式:

jsx 复制代码
const styles = StyleSheet.create({
  taskItem: {
    padding: 16,
    backgroundColor: 'f2f2f2',
    marginVertical: 8,
    borderRadius: 4,
  },
  taskText: {
    fontSize: 18,
  },
});
  1. 数据管理

4.1 定义任务数据结构

定义一个简单的任务数据结构:

javascript 复制代码
const tasks = [
  { id: '1', title: '学习React Native' },
  { id: '2', title: '完成项目文档' },
  // 更多任务...
];

4.2 使用状态管理(可选)

如果你需要管理更复杂的状态或者从API获取数据,可以使用React的useStateuseReducer钩子,或者使用如Redux这样的状态管理库。例如,使用useState

jsx 复制代码
import React, { useState } from 'react';

const TaskListScreen = () => {
  const [tasks, setTasks] = useState([...]); // 初始任务数据
  // 其他逻辑...
};
  1. 实现功能(例如:添加、删除任务)

你可以通过在任务列表中添加按钮或使用其他交互方式来实现添加或删除任务的功能。例如,添加任务:

jsx 复制代码
const addTask = (title) => {
  setTasks([...tasks, { id: Date.now().toString(), title }]); // 添加新任务到列表中
};
  1. 整合与测试

将任务列表组件整合到你的应用中,例如在App.js中:

jsx 复制代码
import React from 'react';
import { View } from 'react-native';
import TaskList from './components/TaskList'; // 假设你的TaskList组件在这里定义
import { tasks } from './data/tasks'; // 假设你的任务数据在这里定义
import AddTaskButton from './components/AddTaskButton'; // 假设你有一个添加任务的按钮组件
import { useState } from 'react'; // 如果需要动态管理状态的话,记得导入useState等钩子函数。

const App = () => {
  const [tasks, setTasks] = useState(tasks); // 使用状态管理任务列表数据。如果数据是静态的,可以移除这行。
  const addTask = (title) => setTasks([...tasks, { id: Date.now().toString(), title }]); // 添加任务的函数。如果不需要动态添加,可以移除这行。
  return (
    <View style={{ flex: 1, padding: 16 }}>
      <AddTaskButton onPress={() => addTask('新任务标题')} /> {/* 如果需要动态添加 */}
      <TaskList

真实案例演示代码:

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代码实现了一个运动类型管理系统,其核心架构基于函数组件和Hooks状态管理机制。系统通过useState Hook维护运动类型数据的状态树,包含运动列表、模态框可见性、编辑状态等关键状态。UI层面采用原生组件构建网格布局,每个运动卡片通过内联样式动态绑定颜色属性,实现视觉差异化。模态框组件通过透明遮罩和滑动动画提供流畅的用户体验,数据持久化可通过集成AsyncStorage实现本地存储。

在鸿蒙生态适配方面,该系统可作为运动健康应用的基础模块。通过ArkTS桥接可实现跨平台运行,利用HarmonyOS分布式能力扩展至多设备协同场景。系统架构支持动态加载运动图标资源,便于后续集成鸿蒙系统的图形渲染优化特性。鸿蒙特有的Ability框架可将该管理系统封装为独立服务能力,通过Intent机制实现应用间数据传递。在UI层面,可借助鸿蒙ArkUI框架的声明式语法重构组件,利用其响应式渲染机制提升性能。系统还可接入鸿蒙生态的健康数据服务,实现运动类型与设备传感器数据的深度融合,构建完整的运动健康管理解决方案。

从技术实现角度看,该系统展现了现代化前端开发的核心理念。状态管理采用单向数据流模式,通过useState Hook实现响应式更新,确保UI与数据同步。组件设计遵循单一职责原则,将展示逻辑与业务逻辑分离,提高代码可维护性。模态框的实现利用了React Native的Modal组件,通过透明背景和滑动动画增强用户体验。运动类型卡片采用Flexbox布局实现响应式网格,适配不同屏幕尺寸。

在鸿蒙系统集成方面,该React Native应用可通过HarmonyOS的React Native支持运行。鸿蒙的分布式数据管理能力可为运动类型数据提供云端同步功能,实现多设备间数据一致性。系统可利用鸿蒙的AbilitySlice机制重构导航结构,通过Page Ability管理不同功能页面。鸿蒙的资源管理框架支持多语言、多分辨率资源适配,提升国际化支持能力。设备间的数据流转可通过鸿蒙的分布式软总线实现,用户可在手机、平板、智能手表等设备间无缝切换运动管理体验。

数据持久化方案在鸿蒙生态中有多种选择。除React Native的AsyncStorage外,还可集成鸿蒙的轻量级数据存储Preference,或使用关系型数据库RelationalStore存储复杂数据结构。对于需要云端同步的数据,可接入鸿蒙的云开发服务AppGallery Connect,实现数据的远程存储和同步。鸿蒙的统一数据管理服务可为运动类型数据提供标准化的数据模型,便于与其他健康应用进行数据交换。


打包

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

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

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

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

相关推荐
小雨下雨的雨12 小时前
Flutter 框架跨平台鸿蒙开发 —— Row & Column 布局之轴线控制艺术
flutter·华为·交互·harmonyos·鸿蒙系统
北辰alk12 小时前
React Consumer 找不到 Provider 的处理方案
react.js
Amumu1213813 小时前
React 前端请求
前端·react.js·okhttp
小雨下雨的雨13 小时前
Flutter 框架跨平台鸿蒙开发 —— Center 控件之完美居中之道
flutter·ui·华为·harmonyos·鸿蒙
小雨下雨的雨13 小时前
Flutter 框架跨平台鸿蒙开发 —— Icon 控件之图标交互美学
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨14 小时前
Flutter 框架跨平台鸿蒙开发 —— Placeholder 控件之布局雏形美学
flutter·ui·华为·harmonyos·鸿蒙系统
行者9614 小时前
OpenHarmony Flutter弹出菜单组件深度实践:从基础到高级的完整指南
flutter·harmonyos·鸿蒙
zuozewei14 小时前
零基础 | 从零实现ReAct Agent:完整技术实现指南
前端·react.js·前端框架·智能体
白柚Y14 小时前
react的hooks
前端·javascript·react.js