RN for OpenHarmony AnimeHub项目实战:历史记录页面开发

案例开源地址:https://atomgit.com/nutpi/Rn_openharmony_AnimeHub

历史记录页展示用户浏览过的动漫,帮助用户找回之前看过的内容。这篇来讲历史记录页的实现,重点是清空功能和确认对话框的使用。

功能设计

历史记录页需要实现以下功能:

  • 列表展示 - 显示所有浏览过的动漫
  • 点击跳转 - 点击动漫跳转到详情页
  • 清空历史 - 一键清空所有历史记录
  • 确认对话框 - 清空前弹出确认
  • 空状态提示 - 没有历史时显示友好提示

这个页面和收藏页结构类似,但多了一个清空功能。历史记录是自动生成的(用户浏览详情页时自动添加),所以需要提供清空功能让用户管理。

历史记录功能在内容型应用中很常见。用户可能看了一部动漫但忘记收藏,通过历史记录可以找回。这是一种"被动收藏"的机制。

组件导入

typescript 复制代码
import React from 'react';
import { View, FlatList, StyleSheet, TouchableOpacity, Text, Alert } from 'react-native';
import { Colors, Spacing, FontSize, BorderRadius } from '../../theme';
import { Anime } from '../../types';
import { AnimeListItem } from '../../components/anime';
import { Header, EmptyState, Icon } from '../../components/common';
import { useStore } from '../../store';

与收藏页的区别:

  • 多导入了 Alert - 用于显示确认对话框
  • 多导入了 TouchableOpacityText - 虽然当前代码没用到,但保留了扩展性

Alert 是 React Native 提供的原生对话框组件。它会调用系统原生的对话框,在 iOS 和 Android 上有不同的样式,但功能一致。

获取历史数据

从全局状态获取历史记录和清空方法:

typescript 复制代码
export const HistoryScreen = ({ navigation }: any) => {
  const { history, clearHistory } = useStore();

说明:

  • history - 浏览历史数组,类型是 Anime[]
  • clearHistory - 清空历史的方法

和收藏不同,历史记录是自动添加的。当用户进入动漫详情页时,该动漫会被自动添加到历史记录中。这个逻辑在详情页或全局状态中实现。

清空确认处理

typescript 复制代码
const handleClear = () => {
  Alert.alert(
    '清空历史',
    '确定要清空所有浏览历史吗?',
    [
      { text: '取消', style: 'cancel' },
      { text: '确定', style: 'destructive', onPress: clearHistory },
    ]
  );
};

Alert.alert 参数:

  • 第一个参数 - 对话框标题
  • 第二个参数 - 对话框内容
  • 第三个参数 - 按钮数组

按钮配置:

  • text - 按钮文字
  • style - 按钮样式('cancel'、'default'、'destructive')
  • onPress - 点击回调

style: 'cancel' 会让按钮显示为取消样式(在 iOS 上是蓝色粗体)。style: 'destructive' 会让按钮显示为危险操作样式(在 iOS 上是红色)。
为什么需要确认对话框?因为清空历史是不可逆的操作。用户可能误触清空按钮,确认对话框可以防止误操作。这是一种"防御性设计"。

列表项渲染

typescript 复制代码
const renderItem = ({ item }: { item: Anime }) => (
  <AnimeListItem
    anime={item}
    onPress={() => navigation.navigate('AnimeDetail', { animeId: item.mal_id })}
  />
);

说明:

  • 和收藏页完全一样
  • 使用 AnimeListItem 组件
  • 点击跳转到详情页

历史记录的顺序是按浏览时间排列的,最近浏览的在最前面。这样用户可以快速找到刚刚看过的动漫。

Header 配置

typescript 复制代码
<Header 
  title="浏览历史" 
  showBack 
  onBack={() => navigation.goBack()}
  rightIcon={history.length > 0 ? 'trash' : undefined}
  onRightPress={handleClear}
/>

配置说明:

  • 标题"浏览历史"
  • 显示返回按钮
  • 右侧图标是垃圾桶(trash)
  • 只有有历史记录时才显示清空按钮

rightIcon={history.length > 0 ? 'trash' : undefined} 是条件渲染。当历史为空时,不显示清空按钮,因为没有东西可以清空。
垃圾桶图标是清空/删除操作的通用图标,用户一看就知道是什么功能。

FlatList 配置

typescript 复制代码
<FlatList
  data={history}
  renderItem={renderItem}
  keyExtractor={item => item.mal_id.toString()}
  contentContainerStyle={[styles.list, history.length === 0 && styles.emptyList]}
  showsVerticalScrollIndicator={false}
  ListEmptyComponent={
    <EmptyState 
      icon="clock" 
      title="暂无历史" 
      subtitle="浏览过的动漫会显示在这里" 
    />
  }
/>

与收藏页的区别:

  • 数据源是 history 而不是 favorites
  • 空状态图标是 clock(时钟)而不是 heart(爱心)
  • 空状态文案不同

时钟图标和历史记录的概念很搭配,都和时间相关。图标的选择要符合功能的语义。

空状态组件

typescript 复制代码
<EmptyState 
  icon="clock" 
  title="暂无历史" 
  subtitle="浏览过的动漫会显示在这里" 
/>

设计说明:

  • 图标用时钟,表示时间/历史
  • 主标题告诉用户当前状态
  • 副标题告诉用户历史是如何产生的

副标题"浏览过的动漫会显示在这里"暗示用户:历史记录是自动生成的,不需要手动添加。这和收藏页的"点击收藏按钮添加"形成对比。

样式定义

typescript 复制代码
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Colors.background,
  },
  list: {
    padding: Spacing.md,
  },
  emptyList: {
    flex: 1,
  },
});

说明:

  • 和收藏页完全一样
  • 简洁的三个样式

收藏页和历史记录页的样式完全一样,这是有意为之的设计。两个页面功能相似,保持一致的视觉风格可以降低用户的学习成本。

历史记录的自动添加

虽然添加历史的代码不在这个文件中,但理解它的实现很重要:

typescript 复制代码
// 在详情页或全局状态中
const addToHistory = (anime: Anime) => {
  set((state) => {
    // 先移除已存在的记录(避免重复)
    const filtered = state.history.filter(h => h.mal_id !== anime.mal_id);
    // 添加到开头
    return { history: [anime, ...filtered].slice(0, 50) };
  });
};

逻辑说明:

  • 先移除已存在的记录(如果有)
  • 把新记录添加到数组开头
  • 限制最多保存 50 条

为什么要先移除再添加?因为用户可能多次浏览同一部动漫。如果不移除,列表中会有重复项。移除后再添加到开头,可以保证最近浏览的在最前面。
slice(0, 50) 限制历史记录的数量。如果不限制,历史记录会无限增长,占用越来越多的存储空间。50 条是一个合理的数量,足够用户回顾,又不会太多。

清空历史的实现

typescript 复制代码
// 在全局状态中
clearHistory: () => {
  set({ history: [] });
},

说明:

  • 直接把 history 设为空数组
  • 非常简单的实现

清空操作是不可逆的。一旦清空,数据就永久删除了。这就是为什么需要确认对话框。

Alert 的跨平台差异

Alert 在不同平台上的表现:

  • iOS - 原生 UIAlertController,支持 cancel、default、destructive 样式
  • Android - 原生 AlertDialog,样式相对简单
  • 鸿蒙 - 使用系统对话框,样式可能有所不同

React Native 的 Alert 是对原生对话框的封装,所以在不同平台上会有不同的外观。这是"原生体验"的一部分,用户会看到熟悉的系统对话框。
如果需要完全一致的跨平台样式,可以使用自定义的 Modal 组件来实现对话框。但原生对话框的优势是性能好、用户熟悉。

与收藏页的对比

收藏页和历史记录页有很多相似之处,也有一些区别:

相似点:

  • 都是列表展示
  • 都使用 AnimeListItem 组件
  • 都有空状态处理
  • 都使用全局状态

区别:

  • 收藏是主动添加,历史是自动添加
  • 收藏没有清空功能,历史有
  • 收藏用爱心图标,历史用时钟图标
  • 收藏没有数量限制,历史限制 50 条

这两个页面可以考虑抽取公共组件或 Hook,减少代码重复。但在教程项目中,保持独立可以让每个页面的代码更清晰完整。

历史记录的价值

历史记录功能的价值:

  • 找回内容 - 用户可以找回之前看过但忘记收藏的动漫
  • 行为分析 - 可以分析用户的浏览偏好(如果需要的话)
  • 快速访问 - 用户可以快速回到最近浏览的内容

历史记录是一种"被动收藏"。用户不需要主动操作,系统自动记录。这降低了用户的操作成本,但也需要提供清空功能让用户管理。

隐私考虑

历史记录涉及用户隐私:

  • 数据存储在本地,不上传服务器
  • 用户可以随时清空
  • 应用卸载后数据会被删除

在实际项目中,如果历史记录需要同步到云端,需要明确告知用户,并提供关闭同步的选项。隐私保护是应用设计的重要考虑因素。

小结

历史记录页和收藏页结构类似,但多了清空功能。清空操作使用 Alert.alert 显示确认对话框,防止用户误操作。

历史记录是自动添加的,当用户浏览动漫详情时自动记录。为了避免无限增长,限制最多保存 50 条记录。

Header 的右侧图标根据历史记录是否为空动态显示,空列表时不显示清空按钮。空状态使用时钟图标,和历史记录的概念呼应。

下一篇会讲设置页面,展示应用的各种设置选项。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
摘星编程3 小时前
React Native for OpenHarmony 实战:Keyboard 键盘事件详解
react native·react.js·计算机外设
摘星编程3 小时前
React Native for OpenHarmony 实战:ToastAndroid 安卓提示详解
android·react native·react.js
摘星编程3 小时前
React Native for OpenHarmony 实战:Clipboard 剪贴板详解
javascript·react native·react.js
摘星编程3 小时前
React Native for OpenHarmony 实战:BackgroundImage 背景视图详解
javascript·react native·react.js
摘星编程3 小时前
React Native for OpenHarmony 实战:TimePickerAndroid 时间选择器详解
javascript·react native·react.js
lili-felicity12 小时前
React Native for Harmony 多功能 Avatar 头像组件 完整实现
react native·react.js·智能手机
2501_9481953414 小时前
RN for OpenHarmony英雄联盟助手App实战:符文配置实现
javascript·react native·react.js
2501_9445210016 小时前
rn_for_openharmony商城项目app实战-商品评价实现
javascript·数据库·react native·react.js·ecmascript·harmonyos