优化联赛选择模态框性能

This commit is contained in:
xianyi
2026-01-14 10:45:22 +08:00
parent c936f3ba6b
commit 7bdbdbaa18
3 changed files with 230 additions and 193 deletions

View File

@@ -5,7 +5,7 @@ import { useTheme } from "@/context/ThemeContext";
import { Image } from "expo-image";
import React from "react";
import { useTranslation } from "react-i18next";
import { Modal, Pressable, ScrollView, StyleSheet, View } from "react-native";
import { FlatList, Modal, Pressable, StyleSheet, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
interface SelectionOption {
@@ -119,6 +119,65 @@ export function SelectionModal({
const isDark = theme === "dark";
const bg = isDark ? "#1C1C1E" : "#FFFFFF";
const text = isDark ? "#FFFFFF" : "#000000";
const optionBg = isDark ? "#2C2C2E" : "#F5F5F5";
const iconBg = isDark ? "#3A3A3C" : "#E5E5EA";
const renderOption = ({ item: opt }: { item: SelectionOption }) => {
const isSelected = selectedValue === opt.value;
// 获取运动键用于i18n和图标
const sportId = typeof opt.id === "number" ? opt.id : undefined;
const sportKey = sportId ? getSportKeyById(sportId) : getSportIconName(opt.label, sportId);
const sportName = t(`sports.${sportKey}`, { defaultValue: opt.label });
return (
<Pressable
style={[styles.option, { backgroundColor: optionBg }]}
onPress={() => {
onSelect(opt.value);
onClose();
}}
>
{/* 左侧图标 */}
<View style={[styles.iconContainer, { backgroundColor: iconBg }]}>
<Image
source={getSportIconSource(opt.label, isSelected, sportId)}
style={styles.iconImage}
contentFit="contain"
/>
</View>
{/* 中间内容 */}
<View style={styles.content}>
<ThemedText
style={[
styles.optionLabel,
{ fontWeight: isSelected ? "600" : "400" },
]}
>
{sportName}
</ThemedText>
<ThemedText style={styles.statusText}>
{isSelected ? t("selection.selected") : t("selection.click_to_toggle")}
</ThemedText>
</View>
{/* 右侧指示点 */}
<View
style={[
styles.indicator,
{
backgroundColor: isSelected
? Colors.light.tint
: isDark
? "#666666"
: "#CCCCCC",
},
]}
/>
</Pressable>
);
};
return (
<Modal
@@ -141,70 +200,18 @@ export function SelectionModal({
<IconSymbol name="close" size={24} color={text} />
</Pressable>
</View>
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
{options.map((opt) => {
const isSelected = selectedValue === opt.value;
const optionBg = isDark ? "#2C2C2E" : "#F5F5F5";
const iconBg = isDark ? "#3A3A3C" : "#E5E5EA";
// 获取运动键用于i18n和图标
const sportId = typeof opt.id === "number" ? opt.id : undefined;
const sportKey = sportId ? getSportKeyById(sportId) : getSportIconName(opt.label, sportId);
const sportName = t(`sports.${sportKey}`, { defaultValue: opt.label });
return (
<Pressable
key={opt.id}
style={[
styles.option,
{ backgroundColor: optionBg },
]}
onPress={() => {
onSelect(opt.value);
onClose();
}}
>
{/* 左侧图标 */}
<View style={[styles.iconContainer, { backgroundColor: iconBg }]}>
<Image
source={getSportIconSource(opt.label, isSelected, sportId)}
style={styles.iconImage}
contentFit="contain"
/>
</View>
{/* 中间内容 */}
<View style={styles.content}>
<ThemedText
style={[
styles.optionLabel,
{ fontWeight: isSelected ? "600" : "400" },
]}
>
{sportName}
</ThemedText>
<ThemedText style={styles.statusText}>
{isSelected ? t("selection.selected") : t("selection.click_to_toggle")}
</ThemedText>
</View>
{/* 右侧指示点 */}
<View
style={[
styles.indicator,
{
backgroundColor: isSelected
? Colors.light.tint
: isDark
? "#666666"
: "#CCCCCC",
},
]}
/>
</Pressable>
);
})}
</ScrollView>
<FlatList
data={options}
renderItem={renderOption}
keyExtractor={(item) => item.id.toString()}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.listContent}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
windowSize={10}
initialNumToRender={10}
/>
</SafeAreaView>
</View>
</Modal>
@@ -238,8 +245,8 @@ const styles = StyleSheet.create({
alignItems: "center",
justifyContent: "center",
},
scrollView: {
maxHeight: 400,
listContent: {
paddingBottom: 20,
},
option: {
flexDirection: "row",