feat: enhance TabLayout and HomeScreen with safe area insets and optimized refresh handling

This commit is contained in:
imeepos
2026-01-26 18:36:18 +08:00
parent 818f6e8591
commit 65b61ce05e
3 changed files with 55 additions and 11 deletions

View File

@@ -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,

View File

@@ -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}

View File

@@ -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={[