Parcourir la source

修复宝箱页面切换闪屏

zbb il y a 3 mois
Parent
commit
fc589826db
1 fichiers modifiés avec 101 ajouts et 83 suppressions
  1. 101 83
      app/(tabs)/box.tsx

+ 101 - 83
app/(tabs)/box.tsx

@@ -1,7 +1,7 @@
 import { Barrage } from '@/components/Barrage';
 import { Image } from 'expo-image';
 import { useRouter } from 'expo-router';
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import {
     ActivityIndicator,
     FlatList,
@@ -38,6 +38,83 @@ interface BarrageItem {
   poolId?: string;
 }
 
+// Static Header Component - Memoized to prevent re-renders
+const StaticHeader = React.memo(({ barrageList }: { barrageList: BarrageItem[] }) => (
+  <View>
+    {/* 占位空间 - 给顶部搜索栏留出空间 */}
+    <View style={{ height: 53 }} />
+
+    {/* 顶部主图 - 绝对定位,叠在背景上 */}
+    <View style={styles.mainImageContainer}>
+      <Image
+        source={{ uri: Images.box.awardMainImg }}
+        style={styles.mainImage}
+        contentFit="fill"
+      />
+    </View>
+
+    {/* 占位空间 - 主图高度 */}
+    <View style={{ height: 360 }} />
+
+    {/* 弹幕区域 */}
+    {barrageList && barrageList.length > 0 && (
+      <View style={styles.barrageSection}>
+        <Barrage data={barrageList.slice(0, Math.ceil(barrageList.length / 2))} />
+        <View style={{ height: 6 }} />
+        <Barrage 
+          data={barrageList.slice(Math.ceil(barrageList.length / 2))} 
+          speed={35} 
+        />
+      </View>
+    )}
+  </View>
+));
+
+// Type Selector Component
+const TypeSelector = React.memo(({ 
+    typeIndex, 
+    priceSort, 
+    onTypeChange, 
+    onSortChange 
+}: { 
+    typeIndex: number;
+    priceSort: number; 
+    onTypeChange: (index: number) => void;
+    onSortChange: () => void;
+}) => (
+    <View style={styles.typeSection}>
+      <View style={styles.typeList}>
+        {typeList.map((item, index) => (
+          <TouchableOpacity
+            key={index}
+            style={styles.typeItem}
+            onPress={() => onTypeChange(index)}
+            activeOpacity={0.7}
+          >
+            <Image
+              source={{ uri: typeIndex === index ? item.imgOn : item.img }}
+              style={styles.typeImage}
+              contentFit="contain"
+            />
+          </TouchableOpacity>
+        ))}
+      </View>
+      <TouchableOpacity style={styles.sortBtn} onPress={onSortChange} activeOpacity={0.7}>
+        <Image
+          source={{
+            uri: priceSort === 0
+              ? Images.box.sortAmount
+              : priceSort === 1
+              ? Images.box.sortAmountOnT
+              : Images.box.sortAmountOnB,
+          }}
+          style={styles.sortIcon}
+          contentFit="contain"
+        />
+      </TouchableOpacity>
+    </View>
+));
+
 export default function BoxScreen() {
   const router = useRouter();
   const insets = useSafeAreaInsets();
@@ -124,18 +201,18 @@ export default function BoxScreen() {
     }
   };
 
-  const handleTypeChange = (index: number) => {
+  const handleTypeChange = useCallback((index: number) => {
     setTypeIndex(index);
     setList([]);
     setCurrent(1);
     setHasMore(true);
-  };
+  }, []);
 
-  const handlePriceSort = () => {
+  const handlePriceSort = useCallback(() => {
     setPriceSort((prev) => (prev + 1) % 3);
-  };
+  }, []);
 
-  const handleItemPress = (item: PoolItem) => {
+  const handleItemPress = useCallback((item: PoolItem) => {
     // 检查商品状态
     if (item.status !== undefined && item.status !== 1) return;
     
@@ -155,9 +232,9 @@ export default function BoxScreen() {
       // 其他商品
       router.push(`/product/${item.id}` as any);
     }
-  };
+  }, [router]);
 
-  const renderItem = ({ item }: { item: PoolItem }) => (
+  const renderItem = useCallback(({ item }: { item: PoolItem }) => (
     <TouchableOpacity
       style={styles.itemContainer}
       onPress={() => handleItemPress(item)}
@@ -171,7 +248,7 @@ export default function BoxScreen() {
         <Image
           source={{ uri: item.cover }}
           style={styles.itemImage}
-          contentFit="cover" // Changed back to 'cover' to fill height/width
+          contentFit="cover" 
         />
         <View style={styles.itemInfo}>
           <Text style={styles.itemName} numberOfLines={1}>{item.name}</Text>
@@ -182,75 +259,21 @@ export default function BoxScreen() {
         </View>
       </ImageBackground>
     </TouchableOpacity>
-  );
-
-  const renderHeader = () => (
-    <View>
-      {/* 占位空间 - 给顶部搜索栏留出空间 */}
-      <View style={{ height: 53 }} />
-
-      {/* 顶部主图 - 绝对定位,叠在背景上 */}
-      <View style={styles.mainImageContainer}>
-        <Image
-          source={{ uri: Images.box.awardMainImg }}
-          style={styles.mainImage}
-          contentFit="fill"
-        />
-      </View>
+  ), [handleItemPress]);
 
-      {/* 占位空间 - 主图高度 */}
-      <View style={{ height: 360 }} />
-
-      {/* 弹幕区域 */}
-      {barrageList && barrageList.length > 0 && (
-        <View style={styles.barrageSection}>
-          <Barrage data={barrageList.slice(0, Math.ceil(barrageList.length / 2))} />
-          <View style={{ height: 6 }} />
-          <Barrage 
-            data={barrageList.slice(Math.ceil(barrageList.length / 2))} 
-            speed={35} 
+  const ListHeader = useMemo(() => (
+      <View>
+          <StaticHeader barrageList={barrageList} />
+          <TypeSelector 
+            typeIndex={typeIndex} 
+            priceSort={priceSort}
+            onTypeChange={handleTypeChange} 
+            onSortChange={handlePriceSort} 
           />
-        </View>
-      )}
-    </View>
-  );
-
-  // 分类筛选单独渲染
-  const renderTypeSection = () => (
-    <View style={styles.typeSection}>
-      <View style={styles.typeList}>
-        {typeList.map((item, index) => (
-          <TouchableOpacity
-            key={index}
-            style={styles.typeItem}
-            onPress={() => handleTypeChange(index)}
-            activeOpacity={0.7}
-          >
-            <Image
-              source={{ uri: typeIndex === index ? item.imgOn : item.img }}
-              style={styles.typeImage}
-              contentFit="contain"
-            />
-          </TouchableOpacity>
-        ))}
       </View>
-      <TouchableOpacity style={styles.sortBtn} onPress={handlePriceSort} activeOpacity={0.7}>
-        <Image
-          source={{
-            uri: priceSort === 0
-              ? Images.box.sortAmount
-              : priceSort === 1
-              ? Images.box.sortAmountOnT
-              : Images.box.sortAmountOnB,
-          }}
-          style={styles.sortIcon}
-          contentFit="contain"
-        />
-      </TouchableOpacity>
-    </View>
-  );
+  ), [barrageList, typeIndex, priceSort, handleTypeChange, handlePriceSort]);
 
-  const renderFooter = () => {
+  const renderFooter = useCallback(() => {
     if (!loading) return null;
     return (
       <View style={styles.footer}>
@@ -258,16 +281,16 @@ export default function BoxScreen() {
         <Text style={styles.footerText}>加载中...</Text>
       </View>
     );
-  };
+  }, [loading]);
 
-  const renderEmpty = () => {
+  const renderEmpty = useCallback(() => {
     if (loading) return null;
     return (
       <View style={styles.empty}>
         <Text style={styles.emptyText}>暂无数据</Text>
       </View>
     );
-  };
+  }, [loading]);
 
   return (
     <View style={styles.container}>
@@ -312,12 +335,7 @@ export default function BoxScreen() {
           data={list}
           renderItem={renderItem}
           keyExtractor={(item) => item.id}
-          ListHeaderComponent={() => (
-            <>
-              {renderHeader()}
-              {renderTypeSection()}
-            </>
-          )}
+          ListHeaderComponent={ListHeader}
           ListFooterComponent={renderFooter}
           ListEmptyComponent={renderEmpty}
           contentContainerStyle={styles.listContent}