使用状态管理、持久化存储或者利用现有的库来辅助React Native鸿蒙跨平台开发开发一个允许用户撤销删除的操作
本文介绍了在React Native中实现类似Windows回收站功能的几种方法。主要内容包括:1) 使用状态管理(useState)维护删除和恢复操作;2) 利用AsyncStorage实现持久化存储,确保应用重启后仍可恢复数据;3) 提供实际代码示例,包含删除/撤销按钮、图标资源和列表渲染逻辑。实现要点是通过维护两个数组(items和deletedItems)来跟踪当前和已删除项目,结合Fla
在 React Native 中,实现一个类似 Windows 回收站的功能(即允许用户撤销删除的操作)可以通过多种方式实现,例如使用状态管理、持久化存储或者利用现有的库来辅助。下面我将介绍几种常见的方法来实现这样一个功能:
- 使用状态管理
最直接的方法是使用 React 的状态(state)来管理组件的删除状态。你可以创建一个数组来存储所有组件的列表,并在删除时将组件标记为已删除但暂时不从数组中移除。
示例代码:
import React, { useState } from 'react';
import { View, Text, Button, FlatList } from 'react-native';
const RecycleBin = ({ items, onDelete, onUndo }) => {
return (
<FlatList
data={items}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Button title="撤销" onPress={() => onUndo(item.id)} />
</View>
)}
/>
);
};
const App = () => {
const [items, setItems] = useState([
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' }
]);
const [deletedItems, setDeletedItems] = useState([]);
const handleDelete = (id) => {
const item = items.find(item => item.id === id);
setDeletedItems(prev => [...prev, item]);
setItems(items.filter(item => item.id !== id));
};
const handleUndo = (id) => {
const item = deletedItems.find(item => item.id === id);
setDeletedItems(deletedItems.filter(item => item.id !== id));
setItems(prev => [...prev, item]);
};
return (
<View>
<FlatList
data={items}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Button title="删除" onPress={() => handleDelete(item.id)} />
</View>
)}
/>
<RecycleBin items={deletedItems} onUndo={handleUndo} />
</View>
);
};
export default App;
- 使用持久化存储(如 AsyncStorage)
对于需要持久化存储删除项的情况,可以使用 AsyncStorage 来保存删除的项。这样即使在应用重启后,用户也可以从回收站恢复已删除的项。
示例代码:
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useState, useEffect } from 'react';
import { View, Text, Button, FlatList } from 'react-native';
const App = () => {
const [items, setItems] = useState([]);
const [deletedItems, setDeletedItems] = useState([]);
const [isLoading, setIsLoading] = useState(true); // 控制加载状态,防止在数据加载完成前渲染组件导致错误。
useEffect(() => {
const loadData = async () => {
try {
const storedItems = await AsyncStorage.getItem('items'); // 假设之前已经存储了数据。
const storedDeletedItems = await AsyncStorage.getItem('deletedItems'); // 假设之前已经存储了数据。
if (storedItems !== null) { setItems(JSON.parse(storedItems)); }
if (storedDeletedItems !== null) { setDeletedItems(JSON.parse(storedDeletedItems)); }
} catch (e) { console.log(e); } finally { setIsLoading(false); } // 加载完成后设置加载状态为false。 避免在数据加载完成前渲染组件。
};
loadData(); // 调用加载数据的函数。 每次组件挂载时都会尝试加载数据。 可以在卸载组件时清除这些数据以节省空间。 例如:componentWillUnmount(){} 中使用 AsyncStorage.clear()。 但在Hooks中使用时,可以使用useEffect的清理函数。 例如:return () => AsyncStorage.clear(); }。
真实实际场景代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Modal, Alert } from 'react-native';
// Base64 图标库
const ICONS = {
trash: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik02IDhoMTJ2MTJhMiAyIDAgMDEtMiAyaC04YTIgMiAwIDAxLTItMlY4eiIvPjxwYXRoIGQ9Ik00IDZoMTZ2Mkg0eiIvPjxwYXRoIGQ9Ik0xMCAxMXY2aDJ2LTZoLTJ6Ii8+PHBhdGggZD0iTTE0IDExdjZoMnYtNkgxNHoiLz48cGF0aCBkPSJNMTAgN2gydjJoLTJWN3oiLz48L3N2Zz4=',
restore: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiA4djRoLTZ2LTZoMnYyaDR6Ii8+PHBhdGggZD0iTTEyIDE0djRoNnYtMmgtMnYtMmgtNHoiLz48cGF0aCBkPSJNMTIgMTB2LTRoLTJ2NGgtNHYyaDR2Mmg0di0yaC00eiIvPjxwYXRoIGQ9Ik02IDE0aC0ydjJoMnYyaDJ2LTJoMnYtMkg2di0yeiIvPjxwYXRoIGQ9Ik0xOCAxMGgydi0yaC0ydi0yaC0ydjJoLTJ2Mmg0eiIvPjxwYXRoIGQ9Ik02IDZoMTJ2MTJINlY2eiIvPjwvc3ZnPg==',
delete: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik02IDE5YzAgMS4xLjkgMiAyIDJoOGMxLjEgMCAyLS45IDItMlY3SDZ2MTJ6bTgtMTBoLTR2LTJoNHYyaDB6Ii8+PHBhdGggZD0iTTE4IDRoLTR2MWgtNHYtMUg2Yy0xLjEgMC0yIC45LTIgMnYxYzAgLjYgMSAxLjUgMSAyaDE0YzAgLS41IDEtMS40IDEtMlY2YzAtMS4xLTEuOS0yLTItMnoiLz48L3N2Zz4=',
file: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik02IDJoMTJ2Nmw2IDZ2MTJINlYyeiIvPjxwYXRoIGQ9Ik0xNCAyaDJ2Nmg2bC02LTZ6Ii8+PC9zdmc+',
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik04LjUgMTMuNWwyLjUgMyAzLjUtNC41IDQuNSA2SDVtMTYtMTVIM2MtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxOGMxLjEgMCAyLS45IDItMlY2YzAtMS4xLS45LTItMi0yeiIvPjwvc3ZnPg==',
document: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xNCAyaC00djJoLTR2MmgxMlY0aC00VjJ6bS00IDZoMTJ2MUgxMHYtMXptMCAyaDEydjFINTB2LTF6bTAgMmgxMnYxSDEwdi0xem0wIDJoMTJ2MUgxMHYtMXoiLz48L3N2Zz4=',
folder: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMCA0SDRjLTEuMSAwLTIgLjktMiAydjEyYzAgMS4xLjkgMiAyIDJoMTZjMS4xIDAgMi0uOSAyLTJWNmMwLTEuMS0uOS0yLTItMmgtOGwtLTItMnoiLz48L3N2Zz4=',
empty: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiA1LjY5Yy0uMzYgMC0uNy4wNS0xLjA2LjEzbDEuNTkgMS41OGMuMDUuMDUuMTMuMDQuMTgtLjAybDEuNTktMS41OGMuMDUtLjA1LjA0LS4xMy0uMDItLjE4Yy0uMzUtLjA4LS43LS4xMy0xLjA2LS4xM3oiLz48cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDgiLz48L3N2Zz4=',
close: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+PC9zdmc+'
};
// 默认文件数据
const DEFAULT_FILES = [
{ id: '1', name: '项目报告.docx', type: 'document', size: '2.4 MB', deletedAt: '2023-06-15 14:30' },
{ id: '2', name: '度假照片.jpg', type: 'image', size: '5.1 MB', deletedAt: '2023-06-14 09:15' },
{ id: '3', name: '财务报表.xlsx', type: 'document', size: '1.2 MB', deletedAt: '2023-06-13 16:45' },
{ id: '4', name: '工作资料', type: 'folder', size: '15.7 MB', deletedAt: '2023-06-12 11:20' },
];
const RecycleBin: React.FC = () => {
const [files, setFiles] = useState(DEFAULT_FILES);
const [selectedFile, setSelectedFile] = useState<any>(null);
const [modalVisible, setModalVisible] = useState(false);
// 获取文件图标
const getFileIcon = (fileType: string) => {
switch (fileType) {
case 'image': return ICONS.image;
case 'document': return ICONS.document;
case 'folder': return ICONS.folder;
default: return ICONS.file;
}
};
// 获取文件图标颜色
const getFileIconColor = (fileType: string) => {
switch (fileType) {
case 'image': return '#45B7D1';
case 'document': return '#4361ee';
case 'folder': return '#f72585';
default: return '#6c757d';
}
};
// 恢复文件
const restoreFile = (id: string) => {
setFiles(files.filter(file => file.id !== id));
Alert.alert('恢复成功', '文件已恢复到原来位置');
};
// 彻底删除文件
const permanentlyDeleteFile = (id: string) => {
Alert.alert(
'彻底删除',
'确定要彻底删除这个文件吗?此操作无法撤销。',
[
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: () => {
setFiles(files.filter(file => file.id !== id));
Alert.alert('删除成功', '文件已彻底删除');
}
}
]
);
};
// 清空回收站
const clearRecycleBin = () => {
if (files.length === 0) {
Alert.alert('提示', '回收站已经是空的');
return;
}
Alert.alert(
'清空回收站',
`确定要彻底删除回收站中的全部${files.length}个项目吗?`,
[
{ text: '取消', style: 'cancel' },
{
text: '清空',
style: 'destructive',
onPress: () => {
setFiles([]);
Alert.alert('清空成功', '回收站已清空');
}
}
]
);
};
// 查看文件详情
const viewFileDetails = (file: any) => {
setSelectedFile(file);
setModalVisible(true);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<View style={styles.headerTop}>
<Text style={styles.title}>回收站</Text>
<TouchableOpacity
style={styles.clearButton}
onPress={clearRecycleBin}
disabled={files.length === 0}
>
<Text style={[styles.clearButtonText, files.length === 0 && styles.disabledText]}>清空</Text>
</TouchableOpacity>
</View>
<Text style={styles.subtitle}>已删除的文件将在此保留30天</Text>
<View style={styles.statsContainer}>
<View style={styles.statBox}>
<Text style={styles.statNumber}>{files.length}</Text>
<Text style={styles.statLabel}>项目</Text>
</View>
<View style={styles.statBox}>
<Text style={styles.statNumber}>
{files.reduce((total, file) => {
const size = parseFloat(file.size);
return total + (isNaN(size) ? 0 : size);
}, 0).toFixed(1)} MB
</Text>
<Text style={styles.statLabel}>占用空间</Text>
</View>
</View>
</View>
<ScrollView contentContainerStyle={styles.content}>
{files.length === 0 ? (
<View style={styles.emptyContainer}>
<Text style={styles.emptyIcon}>{decodeURIComponent(escape(atob(ICONS.empty.split(',')[1])))}</Text>
<Text style={styles.emptyText}>回收站为空</Text>
<Text style={styles.emptySubtext}>没有找到已删除的文件</Text>
</View>
) : (
files.map((file) => (
<View key={file.id} style={styles.fileCard}>
<View style={styles.fileInfo}>
<Text style={[styles.fileIcon, { color: getFileIconColor(file.type) }]}>
{decodeURIComponent(escape(atob(getFileIcon(file.type).split(',')[1])))}
</Text>
<View style={styles.fileDetails}>
<Text style={styles.fileName}>{file.name}</Text>
<Text style={styles.fileMeta}>{file.size} · {file.deletedAt}</Text>
</View>
</View>
<View style={styles.fileActions}>
<TouchableOpacity
style={[styles.actionButton, styles.restoreButton]}
onPress={() => restoreFile(file.id)}
>
<Text style={styles.restoreIcon}>
{decodeURIComponent(escape(atob(ICONS.restore.split(',')[1])))}
</Text>
<Text style={styles.actionText}>恢复</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, styles.deleteButton]}
onPress={() => permanentlyDeleteFile(file.id)}
>
<Text style={styles.deleteIcon}>
{decodeURIComponent(escape(atob(ICONS.delete.split(',')[1])))}
</Text>
<Text style={styles.actionText}>删除</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, styles.infoButton]}
onPress={() => viewFileDetails(file)}
>
<Text style={styles.infoIcon}>...</Text>
</TouchableOpacity>
</View>
</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}>文件详情</Text>
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.closeButton}>×</Text>
</TouchableOpacity>
</View>
{selectedFile && (
<View style={styles.modalBody}>
<Text style={[styles.detailIcon, { color: getFileIconColor(selectedFile.type) }]}>
{decodeURIComponent(escape(atob(getFileIcon(selectedFile.type).split(',')[1])))}
</Text>
<Text style={styles.detailName}>{selectedFile.name}</Text>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>文件类型:</Text>
<Text style={styles.detailValue}>{selectedFile.type}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>文件大小:</Text>
<Text style={styles.detailValue}>{selectedFile.size}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>删除时间:</Text>
<Text style={styles.detailValue}>{selectedFile.deletedAt}</Text>
</View>
</View>
)}
<View style={styles.modalActions}>
<TouchableOpacity
style={[styles.modalButton, styles.modalRestoreButton]}
onPress={() => {
restoreFile(selectedFile?.id);
setModalVisible(false);
}}
>
<Text style={styles.modalButtonText}>恢复文件</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.modalButton, styles.modalDeleteButton]}
onPress={() => {
permanentlyDeleteFile(selectedFile?.id);
setModalVisible(false);
}}
>
<Text style={styles.modalButtonText}>彻底删除</Text>
</TouchableOpacity>
</View>
</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',
},
headerTop: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#212529',
},
clearButton: {
paddingVertical: 6,
paddingHorizontal: 12,
borderRadius: 6,
backgroundColor: '#e9ecef',
},
clearButtonText: {
fontSize: 14,
color: '#495057',
fontWeight: '600',
},
disabledText: {
color: '#adb5bd',
},
subtitle: {
fontSize: 14,
color: '#6c757d',
marginBottom: 15,
},
statsContainer: {
flexDirection: 'row',
backgroundColor: '#e9ecef',
borderRadius: 12,
padding: 15,
},
statBox: {
flex: 1,
alignItems: 'center',
},
statNumber: {
fontSize: 18,
fontWeight: 'bold',
color: '#212529',
},
statLabel: {
fontSize: 14,
color: '#6c757d',
marginTop: 4,
},
content: {
padding: 16,
},
emptyContainer: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 60,
},
emptyIcon: {
fontSize: 64,
color: '#ced4da',
marginBottom: 20,
},
emptyText: {
fontSize: 20,
fontWeight: '600',
color: '#495057',
marginBottom: 8,
},
emptySubtext: {
fontSize: 16,
color: '#6c757d',
},
fileCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
fileInfo: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 15,
},
fileIcon: {
fontSize: 28,
marginRight: 15,
},
fileDetails: {
flex: 1,
},
fileName: {
fontSize: 16,
fontWeight: '600',
color: '#212529',
marginBottom: 4,
},
fileMeta: {
fontSize: 14,
color: '#6c757d',
},
fileActions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
actionButton: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 8,
paddingHorizontal: 12,
borderRadius: 6,
},
restoreButton: {
backgroundColor: '#d1fae5',
},
deleteButton: {
backgroundColor: '#fee2e2',
},
infoButton: {
backgroundColor: '#e0f2fe',
},
restoreIcon: {
fontSize: 16,
color: '#10b981',
marginRight: 6,
},
deleteIcon: {
fontSize: 16,
color: '#ef4444',
marginRight: 6,
},
infoIcon: {
fontSize: 16,
color: '#0ea5e9',
},
actionText: {
fontSize: 14,
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',
},
modalBody: {
alignItems: 'center',
marginBottom: 25,
},
detailIcon: {
fontSize: 48,
marginBottom: 15,
},
detailName: {
fontSize: 18,
fontWeight: '600',
color: '#212529',
marginBottom: 20,
},
detailRow: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
marginBottom: 12,
},
detailLabel: {
fontSize: 16,
color: '#6c757d',
},
detailValue: {
fontSize: 16,
color: '#212529',
fontWeight: '600',
},
modalActions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
modalButton: {
flex: 1,
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
marginHorizontal: 5,
},
modalRestoreButton: {
backgroundColor: '#10b981',
},
modalDeleteButton: {
backgroundColor: '#ef4444',
},
modalButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#ffffff',
},
});
export default RecycleBin;
这段代码实现了一个回收站管理界面,主要用于文件管理应用中的已删除文件管理功能。从鸿蒙开发的角度来看,这个组件体现了现代移动应用开发中的核心设计理念。
在数据结构设计上,DEFAULT_FILES数组采用了ID、名称、类型、大小和删除时间的组合方式来定义文件对象。这种设计在鸿蒙应用开发中同样适用,鸿蒙的ArkTS语言支持类似的对象数组结构,可以使用interface来定义文件对象的类型结构,确保数据的一致性和类型安全。鸿蒙开发中推荐使用资源管理机制来处理图标,将图标文件放置在resources目录下,通过$r(‘app.media.icon_name’)方式引用。
在状态管理方面,React使用useState来维护组件状态,包括文件列表、选中文件、模态框显示状态等。鸿蒙开发中可以使用@State装饰器实现类似的状态管理机制,通过状态变量的变更来驱动UI的自动更新。鸿蒙的声明式UI框架同样具有高效的渲染机制,通过状态变化自动计算最小渲染代价来更新界面。
UI布局采用了卡片列表设计,通过ScrollView容器展示文件卡片。在鸿蒙开发中,可以使用Column和Row组合配合ForEach循环渲染来实现类似的列表布局效果。每个文件卡片包含了文件信息展示区和操作按钮区,这种模块化的设计便于维护和扩展。
文件图标处理根据文件类型动态选择不同的图标和颜色,这种设计在鸿蒙应用中同样重要。鸿蒙支持通过条件渲染来实现类似的功能,根据文件类型动态绑定不同的图标资源和样式属性。颜色管理方面,为不同文件类型指定了特定的主题色,这在鸿蒙应用中可以通过资源文件统一管理颜色值。
模态框的实现体现了良好的用户体验设计,通过透明遮罩来突出操作焦点。鸿蒙系统提供了丰富的弹窗组件,可以实现更加原生和一致的用户交互体验。文件详情查看功能可以通过鸿蒙的Sheet组件或者自定义弹窗来实现。
数据持久化方面,虽然代码中没有直接体现,但在实际应用中会将回收站数据存储在本地或云端。鸿蒙提供了多种数据存储方案,包括Preferences轻量级数据存储、KVStore分布式数据存储等,可以根据应用需求选择合适的存储方式。
在交互设计上,恢复文件和彻底删除操作都提供了确认机制,防止误操作。鸿蒙系统有内置的AlertDialog组件,可以提供更加原生和一致的用户交互体验。清空回收站功能通过批量操作来提高用户效率,这种设计在鸿蒙应用中同样适用。
统计信息展示区域显示了回收站中的项目数量和占用空间,这种数据可视化在鸿蒙应用中可以通过Text组件结合状态计算来实现。鸿蒙支持响应式布局,可以根据屏幕尺寸自动调整统计信息的展示方式。
文件操作方面,恢复和删除功能通过过滤数组来更新文件列表,这种数据处理方式在鸿蒙开发中同样适用。鸿蒙的数组操作API提供了丰富的函数式编程支持,可以实现类似的文件管理逻辑。
空状态处理通过条件渲染展示空回收站提示,这种用户体验设计在鸿蒙应用中同样重要。鸿蒙支持通过if-else条件渲染来实现不同的界面状态,确保用户在任何情况下都有清晰的操作指引。
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

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

最后运行效果图如下显示:
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)