From 3703b20ef5d69ea0183047475dfffb7445553d3c Mon Sep 17 00:00:00 2001 From: Vegas <“qazmm69@gmail.com”> Date: Tue, 11 Nov 2025 17:34:33 +0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=97=E8=A1=A8=E9=95=BF?= =?UTF-8?q?=E5=BA=A6=E4=B8=8D=E5=A4=9F=E6=97=B6=E6=BB=91=E5=8A=A8=E7=A9=BA?= =?UTF-8?q?=E7=99=BD=E5=8C=BA=E5=9F=9F=E6=97=A0=E6=B3=95=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/activity.tsx | 100 ++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/app/(tabs)/activity.tsx b/app/(tabs)/activity.tsx index a6301ab..c9e580a 100644 --- a/app/(tabs)/activity.tsx +++ b/app/(tabs)/activity.tsx @@ -1,15 +1,16 @@ import React, { useState, useRef } from 'react'; -import { - Text, - SafeAreaView, - StyleSheet, - View, - FlatList, - TouchableOpacity, - Dimensions, - Image, - NativeSyntheticEvent, - NativeScrollEvent +import { + Text, + SafeAreaView, + StyleSheet, + View, + FlatList, + TouchableOpacity, + Dimensions, + Image, + NativeSyntheticEvent, + NativeScrollEvent, + LayoutChangeEvent // 导入 LayoutChangeEvent 类型 } from "react-native"; const screenWidth = Dimensions.get('window').width; @@ -32,7 +33,9 @@ const categoryList = [ // 随机生成游戏数据 const generateRandomGames = (categoryId: string, baseName: string) => { - const count = Math.floor(Math.random() * 11) + 1; + // 调整随机数,使其更容易出现空列表或少量item,以测试优化效果 + // (Math.random() * 6) + 0 意味着可能产生 0 到 5 个 + const count = Math.floor(Math.random() * 6); const gameTypes = ['经典版', '豪华版', '至尊版', '竞技版', '休闲版', '大师版', '传奇版', '极速版', '黄金版', '钻石版', '王者版']; const colors = ['4A90E2', 'E94B3C', '50C878', 'F39C12', '9B59B6', 'E74C3C']; @@ -48,7 +51,7 @@ const generateRandomGames = (categoryId: string, baseName: string) => { }; // 模拟数据 -const mockData: Record> = {}; +const mockData: Record> = {}; categoryList.forEach(cat => { mockData[cat.id] = generateRandomGames(cat.id, cat.title); }); @@ -58,6 +61,9 @@ export default function ActivityScreen() { const [isSwitching, setIsSwitching] = useState(false); const rightListRef = useRef(null); + // 【新增】State,用于存储右侧列表容器的实际高度 + const [rightListHeight, setRightListHeight] = useState(0); + // 支持循环切换分类 const handleCategorySwitch = (direction: 'prev' | 'next') => { const currentIndex = categoryList.findIndex(item => item.id === selectedId); @@ -66,6 +72,7 @@ export default function ActivityScreen() { // 切换分类 setSelectedId(categoryList[newIndex].id); + // 切换时重置滚动,避免新列表加载时停在奇怪的位置 rightListRef.current?.scrollToOffset({ offset: 0, animated: false }); }; @@ -76,16 +83,21 @@ export default function ActivityScreen() { const layoutHeight = layoutMeasurement.height; const contentHeight = contentSize.height; + // 如果正在切换中,则锁定,防止短时间内连续触发 if (isSwitching) return; - // 滑到顶部并继续下拉 + // 滑到顶部并继续下拉(-40 是一个触发阈值) if (y < -40) { setIsSwitching(true); handleCategorySwitch('prev'); + // 延迟重置isSwitching状态,给动画和数据加载留出时间 setTimeout(() => setIsSwitching(false), 600); } // 滑到底部并继续上推 + // contentHeight <= layoutHeight 检查内容是否填满 + // (如果没填满) y > 40 检查是否在空白处上拉 + // (如果已填满) y + layoutHeight - contentHeight > 40 检查是否滚动到底部并继续上拉 if (contentHeight <= layoutHeight ? y > 40 : y + layoutHeight - contentHeight > 40) { setIsSwitching(true); handleCategorySwitch('next'); @@ -93,6 +105,14 @@ export default function ActivityScreen() { } }; + // 【新增】onLayout 事件处理函数 + // 当右侧容器布局时,获取其高度 + const onRightListLayout = (event: LayoutChangeEvent) => { + const { height } = event.nativeEvent.layout; + setRightListHeight(height); + }; + + const LeftItem = ({ id, title, icon, isSelected, onPress }: any) => ( {/* 右侧内容 */} - - ( - - )} - keyExtractor={item => item.id} - showsVerticalScrollIndicator={false} - bounces={true} - scrollEventThrottle={16} - onScroll={handleScroll} - contentContainerStyle={styles.rightListContent} - /> + {/* 【优化点 1】给容器添加 onLayout 来获取高度 */} + + {/* 只有在获取到高度后才渲染 FlatList,确保 minHeight 初始值正确 */} + {rightListHeight > 0 && ( + ( + + )} + keyExtractor={item => item.id} + showsVerticalScrollIndicator={false} + // bounces 必须为 true (默认值) + bounces={true} + scrollEventThrottle={16} + onScroll={handleScroll} + + // 【优化点 2】为 iOS 添加,使其在内容不足时也能反弹 + alwaysBounceVertical={true} + + // 【优化点 3】设置 contentContainerStyle + contentContainerStyle={[ + styles.rightListContent, + { + // 确保内容容器的最小高度 + // 始终比 FlatList 视图本身高 1 像素 + minHeight: rightListHeight + 1 + } + ]} + /> + )} @@ -194,7 +231,10 @@ const styles = StyleSheet.create({ borderBottomRightRadius: 2, }, contentRight: { flex: 1, backgroundColor: '#f5f5f5' }, - rightListContent: { padding: 12, paddingBottom: 20 }, + rightListContent: { + padding: 12, + paddingBottom: 20 + }, rightItem: { backgroundColor: '#fff', borderRadius: 8, @@ -221,4 +261,4 @@ const styles = StyleSheet.create({ hotBadge: { backgroundColor: '#ff4d4f', paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4 }, hotText: { color: '#fff', fontSize: 10, fontWeight: '600' }, rightItemDesc: { fontSize: 13, color: '#999' }, -}); +}); \ No newline at end of file