import React, { useCallback } from "react"; import { View, StyleSheet, ScrollView, ActivityIndicator } from "react-native"; import { ThemedText } from "@/components/ThemedText"; import { useResponsiveLayout } from "@/hooks/useResponsiveLayout"; import { getCommonResponsiveStyles } from "@/utils/ResponsiveStyles"; interface CustomScrollViewProps { data: any[]; renderItem: ({ item, index }: { item: any; index: number }) => React.ReactNode; numColumns?: number; // 如果不提供,将使用响应式默认值 loading?: boolean; loadingMore?: boolean; error?: string | null; onEndReached?: () => void; loadMoreThreshold?: number; emptyMessage?: string; ListFooterComponent?: React.ComponentType | React.ReactElement | null; } const CustomScrollView: React.FC = ({ data, renderItem, numColumns, // 现在可选,如果不提供将使用响应式默认值 loading = false, loadingMore = false, error = null, onEndReached, loadMoreThreshold = 200, emptyMessage = "暂无内容", ListFooterComponent, }) => { const responsiveConfig = useResponsiveLayout(); const commonStyles = getCommonResponsiveStyles(responsiveConfig); // 使用响应式列数,如果没有明确指定的话 const effectiveColumns = numColumns || responsiveConfig.columns; const handleScroll = useCallback( ({ nativeEvent }: { nativeEvent: any }) => { const { layoutMeasurement, contentOffset, contentSize } = nativeEvent; const isCloseToBottom = layoutMeasurement.height + contentOffset.y >= contentSize.height - loadMoreThreshold; if (isCloseToBottom && !loadingMore && onEndReached) { onEndReached(); } }, [onEndReached, loadingMore, loadMoreThreshold] ); const renderFooter = () => { if (ListFooterComponent) { if (React.isValidElement(ListFooterComponent)) { return ListFooterComponent; } else if (typeof ListFooterComponent === "function") { const Component = ListFooterComponent as React.ComponentType; return ; } return null; } if (loadingMore) { return ; } return null; }; if (loading) { return ( ); } if (error) { return ( {error} ); } if (data.length === 0) { return ( {emptyMessage} ); } // 动态样式 const dynamicStyles = StyleSheet.create({ listContent: { paddingBottom: responsiveConfig.spacing * 2, paddingHorizontal: responsiveConfig.spacing / 2, }, gridContainer: { flexDirection: "row", flexWrap: "wrap", justifyContent: "space-evenly", }, itemContainer: { width: responsiveConfig.cardWidth, marginBottom: responsiveConfig.spacing, }, }); return ( {data.length > 0 ? ( <> {data.map((item, index) => ( {renderItem({ item, index })} ))} {renderFooter()} ) : ( {emptyMessage} )} ); }; export default CustomScrollView;