feat: enhance TabLayout and HomeScreen with safe area insets and optimized refresh handling
This commit is contained in:
@@ -3,12 +3,14 @@ import React from 'react'
|
||||
import { StyleSheet, View, Text } from 'react-native'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
|
||||
import { HomeIcon, MessageIcon, MyIcon, VideoIcon } from '@/components/icon'
|
||||
|
||||
export default function TabLayout() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const insets = useSafeAreaInsets()
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
@@ -19,9 +21,9 @@ export default function TabLayout() {
|
||||
tabBarShowLabel: true,
|
||||
tabBarStyle: {
|
||||
backgroundColor: '#1C1E22CC',
|
||||
height: 83,
|
||||
height: 84 + insets.bottom,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
borderTopWidth: 0,
|
||||
|
||||
@@ -115,15 +115,13 @@ export default function HomeScreen() {
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
// 下拉刷新处理
|
||||
// 下拉刷新处理 - 只刷新当前分类的模板
|
||||
const handleRefresh = useCallback(async () => {
|
||||
if (!selectedCategoryId) return
|
||||
setRefreshing(true)
|
||||
await Promise.all([
|
||||
loadCategories(),
|
||||
selectedCategoryId ? loadTemplates({ categoryId: selectedCategoryId }) : Promise.resolve(),
|
||||
])
|
||||
await loadTemplates({ categoryId: selectedCategoryId })
|
||||
setRefreshing(false)
|
||||
}, [selectedCategoryId, loadCategories, loadTemplates])
|
||||
}, [selectedCategoryId, loadTemplates])
|
||||
|
||||
// 加载更多处理
|
||||
const handleEndReached = useCallback(() => {
|
||||
@@ -289,7 +287,7 @@ export default function HomeScreen() {
|
||||
numColumns={NUM_COLUMNS}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
contentContainerStyle={[styles.scrollContent, { paddingBottom: 60 + insets.bottom + 20 }]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScroll={(e) => handleScroll(e.nativeEvent.contentOffset.y)}
|
||||
scrollEventThrottle={Platform.OS === 'ios' ? 16 : 50}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useRef, useEffect, useCallback } from 'react'
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@@ -6,9 +6,15 @@ import {
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
ViewStyle,
|
||||
Dimensions,
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native'
|
||||
import { DownArrowIcon } from '@/components/icon'
|
||||
|
||||
const SCREEN_WIDTH = Dimensions.get('window').width
|
||||
const ARROW_WIDTH = 48 // 箭头按钮宽度 (padding 8 * 2 + icon ~24 + marginLeft 8)
|
||||
const HORIZONTAL_PADDING = 16
|
||||
|
||||
interface TabNavigationProps {
|
||||
tabs: string[]
|
||||
activeIndex: number
|
||||
@@ -30,6 +36,42 @@ export function TabNavigation({
|
||||
wrapperStyle,
|
||||
onLayout,
|
||||
}: TabNavigationProps): JSX.Element {
|
||||
const scrollViewRef = useRef<ScrollView>(null)
|
||||
const tabLayouts = useRef<{ x: number; width: number }[]>([])
|
||||
|
||||
// 计算可用的滚动区域宽度
|
||||
const scrollAreaWidth = SCREEN_WIDTH - HORIZONTAL_PADDING * 2 - (showArrow ? ARROW_WIDTH : 0)
|
||||
|
||||
// 滚动到激活的 tab 使其居中
|
||||
const scrollToActiveTab = useCallback((index: number) => {
|
||||
const layout = tabLayouts.current[index]
|
||||
if (!layout || !scrollViewRef.current) return
|
||||
|
||||
// 计算让 tab 居中需要的滚动位置
|
||||
const tabCenter = layout.x + layout.width / 2
|
||||
const scrollX = tabCenter - scrollAreaWidth / 2
|
||||
|
||||
scrollViewRef.current.scrollTo({
|
||||
x: Math.max(0, scrollX),
|
||||
animated: true,
|
||||
})
|
||||
}, [scrollAreaWidth])
|
||||
|
||||
// 当 activeIndex 变化时滚动
|
||||
useEffect(() => {
|
||||
// 延迟执行确保布局已完成
|
||||
const timer = setTimeout(() => {
|
||||
scrollToActiveTab(activeIndex)
|
||||
}, 50)
|
||||
return () => clearTimeout(timer)
|
||||
}, [activeIndex, scrollToActiveTab])
|
||||
|
||||
// 记录每个 tab 的布局
|
||||
const handleTabLayout = useCallback((index: number, event: LayoutChangeEvent) => {
|
||||
const { x, width } = event.nativeEvent.layout
|
||||
tabLayouts.current[index] = { x, width }
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<View
|
||||
testID="tab-navigation"
|
||||
@@ -45,6 +87,7 @@ export function TabNavigation({
|
||||
>
|
||||
<View style={styles.tabsContainer}>
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.tabsScrollContent}
|
||||
@@ -55,6 +98,7 @@ export function TabNavigation({
|
||||
testID={`tab-${index}`}
|
||||
style={[styles.tab, activeIndex === index && styles.activeTab]}
|
||||
onPress={() => onTabPress(index)}
|
||||
onLayout={(e) => handleTabLayout(index, e)}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
|
||||
Reference in New Issue
Block a user