
历史记录页展示用户浏览过的动漫,帮助用户找回之前看过的内容。这篇来讲历史记录页的实现,重点是清空功能和确认对话框的使用。
功能设计
历史记录页需要实现以下功能:
- 列表展示 - 显示所有浏览过的动漫
- 点击跳转 - 点击动漫跳转到详情页
- 清空历史 - 一键清空所有历史记录
- 确认对话框 - 清空前弹出确认
- 空状态提示 - 没有历史时显示友好提示
这个页面和收藏页结构类似,但多了一个清空功能。历史记录是自动生成的(用户浏览详情页时自动添加),所以需要提供清空功能让用户管理。
历史记录功能在内容型应用中很常见。用户可能看了一部动漫但忘记收藏,通过历史记录可以找回。这是一种"被动收藏"的机制。
组件导入
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- 用于显示确认对话框 - 多导入了
TouchableOpacity和Text- 虽然当前代码没用到,但保留了扩展性
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