在React Native中开发一个可视化喝水体验的应用程序,你可以通过结合使用UI组件库(如react-native-elementsnative-base等),以及可能的第三方库(如react-native-svg用于绘制图标和图形)来实现。下面是一些步骤和组件,可以帮助你创建一个可视化的喝水提醒和追踪应用:

  1. 初始化项目

首先,确保你已经安装了React Native环境。如果还没有安装,你可以通过以下命令来初始化一个新项目:

npx react-native init DrinkWaterApp
cd DrinkWaterApp
  1. 安装依赖

安装一些有用的库,比如react-native-svg用于绘制图形和react-native-vector-icons用于图标:

npm install react-native-svg react-native-vector-icons
  1. 创建组件

3.1 玻璃杯组件

创建一个表示玻璃杯的组件,使用react-native-svg来绘制玻璃杯的形状和水量。

import React from 'react';
import Svg, { G, Path, Circle } from 'react-native-svg';
import { View, StyleSheet } from 'react-native';

const Glass = ({ level }) => {
  const height = 200; // 玻璃杯高度
  const radius = 20; // 玻璃杯底部半径
  const waterHeight = height * level; // 水位高度,根据level计算

  return (
    <View style={styles.container}>
      <Svg height={height} width={100}>
        <Path // 玻璃杯的外形
          d={`M50,0 C${radius},0 ${radius},${radius} 0,${radius} L0,${height - radius} C0,${height} ${radius},${height} ${radius},${height - radius} L${100 - radius},${height - radius} C${100},${height} ${100},${height} ${100},${height - radius} L100,${radius} C100,${radius} ${100 - radius},0 ${100 - radius},0 Z`}
          fill="transparent"
          stroke="blue"
          strokeWidth={2}
        />
        <Path // 水位
          d={`M50,${height - waterHeight} C${radius},${height - waterHeight} ${radius},${height} 0,${height} L0,${radius} C0,${radius} ${radius},0 ${radius},0 Z`}
          fill="blue"
        />
      </Svg>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default Glass;

3.2 主界面组件

在你的主界面组件中使用这个玻璃杯组件,并添加一些交互功能,如按钮来增加水量。

import React, { useState } from 'react';
import { View, Button, Text } from 'react-native';
import Glass from './Glass'; // 确保路径正确

const App = () => {
  const [level, setLevel] = useState(0.2); // 水位,从0到1之间变化

  const drinkMore = () => {
    setLevel(prevLevel => Math.min(1, prevLevel + 0.1)); // 增加水量,但不超过100%
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>你的饮水目标:</Text>
      <Glass level={level} />
      <Button title="喝一杯水" onPress={drinkMore} />
    </View>
  );
};

export default App;
  1. 运行应用

使用以下命令来运行你的应用:

npx react-native run-Harmony 或者 npx react-native run-Harmony,取决于你的开发环境。

这样,你就创建了一个基本的可视化喝水提醒应用。用户可以通过点击按钮来模拟喝水的动作,玻璃杯中的水会相应地增加。你可以根据需要扩展更多功能,如设置每日目标、提醒功能等。


真实案例用户测试代码:

// app.tsx
import React, { useState, useEffect } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Alert, Modal, Pressable } from 'react-native';

// Base64 图标库
const ICONS = {
  waterDrop: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyMkM2LjQ4IDAgMiA0LjQ4IDIgMTBzMTEuNSAxNi4wMSAxMiAxNmM0LjQ5LTEuMDcgOC01LjUxIDgtMTBzLTQuNDgtMTAtMTAtMTB6Ii8+PC9zdmc+',
  add: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSAxM2gtNnY2aC0ydi02SDV2LTJoNlY1aDJ2Nmg2djJ6Ii8+PC9zdmc+',
  stats: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0zIDEzLjI1aDV2LTVIM3Y1em0wLTRoNVY0SDN2NXptNiA0aDV2LTVIOXY1em0wLTRoNVY0SDl2NXptNiA0aDV2LTVoLTV2NXptMC00aDVWNWgtNXY1eiIvPjwvc3ZnPg==',
  goal: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyLjA0TDMgNi45OCAzIDEyaDE4VjYuOTdMMTIgMi4wNHpNMjEgMTVIN2MtMS4xIDAtMiAuOS0yIDJ2M2gydi0zaDE0djE0SDd2LTNoLTJ2M2MwIDEuMS45IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjE3YzAtMS4xLS45LTItMi0yeiIvPjwvc3ZnPg==',
  settings: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOS4xNCAxMi4wMmMtLjA0LS4zMi0uMDctLjY0LS4wNy0uOTggMC0uMzQuMDMtLjY2LjA3LS45OGwyLjExIDEuNTljLjA3LjA0LjA4LjEzLjAzLjIxbC0xLjk0IDEuNTJjLS4wNS4wNS0uMTMuMDQtLjE4LS4wMnpNNy4wNyAxOC42OWMuMzUtLjA4LjctLjEzIDEuMDYtLjEzcy43MS4wNSAxLjA2LjEzbC0xLjU5IDEuNTljLS4wNS4wNS0uMTMuMDQtLjE4LS4wMmwtMS41OS0xLjU4Yy0uMDUtLjA1LS4wNC0uMTMuMDItLjE4ek0xMiA1LjY5Yy0uMzYgMC0uNy4wNS0xLjA2LjEzbDEuNTkgMS41OGMuMDUuMDUuMTMuMDQuMTgtLjAybDEuNTktMS41OGMuMDUtLjA1LjA0LS4xMy0uMDItLjE4Yy0uMzUtLjA4LS43LS4xMy0xLjA2LS4xM3oiLz48cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDgiLz48L3N2Zz4=',
  close: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+PC9zdmc+'
};

const WaterTracker: React.FC = () => {
  // 应用状态
  const [waterIntake, setWaterIntake] = useState(0); // 已喝水量 (毫升)
  const [dailyGoal, setDailyGoal] = useState(2000); // 每日目标 (毫升)
  const [showSettings, setShowSettings] = useState(false);
  const [newGoal, setNewGoal] = useState(dailyGoal.toString());
  
  // 预设饮水量选项
  const waterOptions = [
    { amount: 100, label: '100ml' },
    { amount: 200, label: '200ml' },
    { amount: 250, label: '250ml' },
    { amount: 300, label: '300ml' },
    { amount: 500, label: '500ml' }
  ];

  // 添加饮水量
  const addWater = (amount: number) => {
    setWaterIntake(prev => prev + amount);
  };

  // 重置每日记录
  const resetDay = () => {
    setWaterIntake(0);
  };

  // 保存新目标
  const saveGoal = () => {
    const goal = parseInt(newGoal);
    if (goal > 0) {
      setDailyGoal(goal);
      setShowSettings(false);
    } else {
      Alert.alert('无效值', '请输入有效的目标水量');
    }
  };

  // 计算完成百分比
  const percentage = Math.min(100, Math.round((waterIntake / dailyGoal) * 100));

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.scrollContainer}>
        {/* 头部标题 */}
        <View style={styles.header}>
          <Text style={styles.title}>💧 喝水小助手</Text>
          <Text style={styles.subtitle}>每天八杯水,健康你我他</Text>
        </View>

        {/* 进度卡片 */}
        <View style={styles.progressCard}>
          <View style={styles.progressHeader}>
            <Text style={styles.progressTitle}>今日饮水进度</Text>
            <TouchableOpacity onPress={() => setShowSettings(true)}>
              <Text style={styles.settingsIcon}>⚙️</Text>
            </TouchableOpacity>
          </View>
          
          <View style={styles.progressBarContainer}>
            <View style={[styles.progressBar, { width: `${percentage}%` }]} />
          </View>
          
          <View style={styles.progressInfo}>
            <Text style={styles.progressText}>{waterIntake}ml / {dailyGoal}ml</Text>
            <Text style={styles.percentageText}>{percentage}%</Text>
          </View>
        </View>

        {/* 快捷添加按钮 */}
        <View style={styles.quickAddSection}>
          <Text style={styles.sectionTitle}>快速添加</Text>
          <View style={styles.quickButtonsContainer}>
            {waterOptions.map((option, index) => (
              <TouchableOpacity 
                key={index} 
                style={styles.quickButton}
                onPress={() => addWater(option.amount)}
              >
                <Text style={styles.quickButtonText}>{option.label}</Text>
              </TouchableOpacity>
            ))}
          </View>
        </View>

        {/* 功能卡片 */}
        <View style={styles.featuresContainer}>
          <TouchableOpacity style={styles.featureCard} onPress={resetDay}>
            <Text style={styles.featureIcon}>🔄</Text>
            <Text style={styles.featureText}>重置今日</Text>
          </TouchableOpacity>
          
          <TouchableOpacity style={styles.featureCard} onPress={() => addWater(250)}>
            <Text style={styles.featureIcon}></Text>
            <Text style={styles.featureText}>添加一杯</Text>
          </TouchableOpacity>
          
          <TouchableOpacity style={styles.featureCard}>
            <Text style={styles.featureIcon}>📊</Text>
            <Text style={styles.featureText}>查看统计</Text>
          </TouchableOpacity>
        </View>

        {/* 提醒信息 */}
        <View style={styles.reminderCard}>
          <Text style={styles.reminderTitle}>💡 健康提醒</Text>
          <Text style={styles.reminderText}>
            成年人每天建议饮水1500-2000ml,相当于8杯水。适量饮水有助于新陈代谢、排毒养颜。
          </Text>
        </View>
      </ScrollView>

      {/* 设置模态框 */}
      <Modal
        animationType="slide"
        transparent={true}
        visible={showSettings}
        onRequestClose={() => setShowSettings(false)}
      >
        <View style={styles.modalOverlay}>
          <View style={styles.modalContent}>
            <View style={styles.modalHeader}>
              <Text style={styles.modalTitle}>设置每日目标</Text>
              <TouchableOpacity onPress={() => setShowSettings(false)}>
                <Text style={styles.closeButton}>×</Text>
              </TouchableOpacity>
            </View>
            
            <View style={styles.inputContainer}>
              <Text style={styles.inputLabel}>目标水量 (毫升)</Text>
              <View style={styles.inputWrapper}>
                <TextInput
                  style={styles.input}
                  value={newGoal}
                  onChangeText={setNewGoal}
                  keyboardType="numeric"
                />
                <Text style={styles.mlLabel}>ml</Text>
              </View>
            </View>
            
            <TouchableOpacity style={styles.saveButton} onPress={saveGoal}>
              <Text style={styles.saveButtonText}>保存设置</Text>
            </TouchableOpacity>
          </View>
        </View>
      </Modal>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f9ff',
  },
  scrollContainer: {
    padding: 20,
  },
  header: {
    alignItems: 'center',
    marginBottom: 30,
    marginTop: 20,
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    color: '#0ea5e9',
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: '#64748b',
  },
  progressCard: {
    backgroundColor: '#ffffff',
    borderRadius: 20,
    padding: 20,
    marginBottom: 25,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 3,
  },
  progressHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 15,
  },
  progressTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0f172a',
  },
  settingsIcon: {
    fontSize: 24,
  },
  progressBarContainer: {
    height: 20,
    backgroundColor: '#e2e8f0',
    borderRadius: 10,
    overflow: 'hidden',
    marginBottom: 15,
  },
  progressBar: {
    height: '100%',
    backgroundColor: '#0ea5e9',
    borderRadius: 10,
  },
  progressInfo: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  progressText: {
    fontSize: 16,
    color: '#334155',
    fontWeight: '600',
  },
  percentageText: {
    fontSize: 16,
    color: '#0ea5e9',
    fontWeight: 'bold',
  },
  quickAddSection: {
    marginBottom: 25,
  },
  sectionTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0f172a',
    marginBottom: 15,
  },
  quickButtonsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-between',
  },
  quickButton: {
    backgroundColor: '#ffffff',
    width: '48%',
    paddingVertical: 15,
    paddingHorizontal: 10,
    borderRadius: 15,
    marginBottom: 12,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.05,
    shadowRadius: 4,
    elevation: 2,
  },
  quickButtonText: {
    fontSize: 16,
    color: '#0ea5e9',
    fontWeight: '600',
  },
  featuresContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 25,
  },
  featureCard: {
    backgroundColor: '#ffffff',
    width: '31%',
    paddingVertical: 20,
    borderRadius: 15,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.05,
    shadowRadius: 4,
    elevation: 2,
  },
  featureIcon: {
    fontSize: 28,
    marginBottom: 8,
  },
  featureText: {
    fontSize: 14,
    color: '#334155',
    fontWeight: '600',
  },
  reminderCard: {
    backgroundColor: '#dbeafe',
    borderRadius: 15,
    padding: 20,
    borderLeftWidth: 5,
    borderLeftColor: '#3b82f6',
  },
  reminderTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e40af',
    marginBottom: 8,
  },
  reminderText: {
    fontSize: 14,
    color: '#1e3a8a',
    lineHeight: 22,
  },
  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: '#0f172a',
  },
  closeButton: {
    fontSize: 30,
    color: '#94a3b8',
    fontWeight: '200',
  },
  inputContainer: {
    marginBottom: 25,
  },
  inputLabel: {
    fontSize: 16,
    color: '#334155',
    marginBottom: 10,
    fontWeight: '600',
  },
  inputWrapper: {
    flexDirection: 'row',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 12,
    paddingHorizontal: 15,
  },
  input: {
    flex: 1,
    fontSize: 18,
    paddingVertical: 12,
  },
  mlLabel: {
    fontSize: 18,
    color: '#64748b',
    fontWeight: '600',
  },
  saveButton: {
    backgroundColor: '#0ea5e9',
    paddingVertical: 15,
    borderRadius: 12,
    alignItems: 'center',
  },
  saveButtonText: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#ffffff',
  },
});

export default WaterTracker;

这段React Native车辆保养日历组件代码实现了一个功能完整的月视图日历系统,其核心原理基于React的状态管理和日期处理机制。getDaysInMonth函数通过精密的日期运算构建了一个标准的6行7列日历网格,这种设计确保了日历显示的一致性和美观性。函数首先计算当前月份的第一天和最后一天,获取月份总天数和起始星期,然后通过三个循环分别处理上个月末尾日期、当前月日期和下个月开始日期的填充,这种分段处理方式在鸿蒙系统的多设备适配中具有重要意义,能够确保不同屏幕尺寸下日历布局的统一性。

从鸿蒙系统适配的角度深入分析,该代码充分利用了React Native的跨平台特性,在鸿蒙设备上能够获得原生级的性能表现。鸿蒙系统的分布式数据管理能力与React的状态提升概念高度契合,events状态作为单一数据源存储所有保养事件数据,确保了数据在不同组件间的一致性。通过localStorage或AsyncStorage实现的数据持久化机制,能够在鸿蒙系统的应用生命周期中保持数据的持久性,即使应用被杀死或设备重启,用户的保养记录也不会丢失。

日期选择功能通过selectedDate状态管理当前选中日期,当用户点击日历中的某个日期时,通过setSelectedDate更新状态并触发界面重新渲染。这种响应式更新机制在鸿蒙系统的触摸交互中能够提供流畅的用户体验,鸿蒙系统对触摸事件的优化处理使得日期选择操作具有良好的响应性。事件检测函数hasEvents通过formatDate函数将日期对象转换为标准的YYYY-MM-DD格式字符串,然后在events对象中查找对应日期的事件记录,这种数据结构设计在鸿蒙系统的高效数据检索中具有优势。

请添加图片描述

日历导航功能通过changeMonth函数实现月份切换,函数内部使用函数式更新确保状态变更的准确性,避免了闭包陷阱问题。在鸿蒙系统的分布式任务调度中,这种状态管理模式能够确保日历组件在不同设备间的连续性操作体验。月份显示区域通过getFullYear和getMonth方法动态获取年份和月份信息,通过中文格式化显示给用户,这种本地化处理方式符合鸿蒙系统对多语言支持的要求。

UI渲染逻辑通过map方法遍历days数组生成42个日期单元格,每个单元格根据isCurrentMonth属性应用不同的样式,非当前月日期使用灰色显示,当前月日期使用主要颜色显示。选中日期通过isSelected状态应用高亮样式,这种视觉反馈机制在鸿蒙系统的UI设计中非常重要,能够帮助用户清晰识别当前操作状态。事件指示器通过hasEvent函数检测日期是否有保养记录,如果有则显示一个小圆点图标,这种设计在鸿蒙系统的健康数据展示中非常常见,能够直观地提醒用户有重要事件需要关注。

星期标题区域通过weekdays数组映射生成7个标签,周日和周六使用特殊颜色显示,这种设计符合中文用户的阅读习惯。日历网格采用flex布局实现响应式设计,能够自适应不同尺寸的鸿蒙设备屏幕,在手机、平板等设备上都能保持良好的显示效果。事件详情区域通过条件渲染显示选中日期的保养安排,当没有选中日期时显示提示信息,这种用户体验设计在鸿蒙系统的应用开发中被广泛采用。


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述

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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐