This commit is contained in:
oneao committed 2026-04-09 21:37:11 +08:00
1 parent 7823fe2768
commit 6b216ffdab
61 files changed
+10206 -2127

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack />;
}
+318
View File
@@ -0,0 +1,318 @@
import { Feather, Ionicons } from '@expo/vector-icons';
import { BlurView } from "expo-blur";
import React, { useState } from "react";
import {
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
export default function QQStyleTabBar() {
const [activeTab, setActiveTab] = useState("消息");
const [unreadCount] = useState(12);
const [hasNewMoment] = useState(true);
const tabs = [
{ id: "消息", label: "消息", icon: "chatbubble" },
{ id: "频道", label: "频道", icon: "people" },
{ id: "联系人", label: "联系人", icon: "person" },
{ id: "动态", label: "动态", icon: "refresh-cw" },
];
return (
<View style={styles.container}>
{/* 模拟聊天列表 - 作为模糊背景 */}
<ScrollView
contentContainerStyle={{ paddingTop: 40, paddingBottom: 100 }}
showsVerticalScrollIndicator={false}
>
{Array.from({ length: 20 }).map((_, i) => {
const isMe = i % 2 === 0;
const messageType = i % 3;
return (
<View
key={i}
style={[
styles.messageRow,
isMe ? styles.right : styles.left,
]}
>
<View style={styles.avatarContainer}>
<View style={styles.avatar} />
</View>
<View
style={[
styles.bubble,
isMe ? styles.myBubble : styles.otherBubble,
]}
>
<Text style={[
styles.msgText,
isMe && styles.myMsgText
]}>
{isMe
? `我发的消息 ${i + 1}`
: messageType === 0
? `你好,最近怎么样? ${i + 1}`
: messageType === 1
? `今晚有空吗?一起吃个饭? ${i + 1}`
: `上次说的那个项目有进展了 ${i + 1}`}
</Text>
<Text style={styles.timeText}>昨天 14:3{i}</Text>
</View>
</View>
);
})}
</ScrollView>
{/* TabBar - 使用expo-blur实现毛玻璃效果 */}
<View style={styles.tabBarContainer}>
<BlurView
intensity={80} // 稍微降低强度,让白色更明显
tint="light"
style={StyleSheet.absoluteFill}
/>
{/* 磨砂白色层 - 调整透明度 */}
<View style={styles.whiteOverlay} />
{/* TabBar内容 */}
<View style={styles.tabs}>
{tabs.map((tab) => {
const isActive = activeTab === tab.id;
const isMessageTab = tab.id === "消息";
const isMomentTab = tab.id === "动态";
return (
<TouchableOpacity
key={tab.id}
style={styles.tabItem}
onPress={() => setActiveTab(tab.id)}
activeOpacity={0.7}
>
<View style={styles.iconContainer}>
{isMessageTab || tab.id === "联系人" || tab.id === "频道" ? (
isActive ? (
<Ionicons
name={tab.icon}
size={24}
color="#0066FF"
/>
) : (
<Ionicons
name={`${tab.icon}-outline`}
size={24}
color="#000000"
/>
)
) : (
<Feather
name={tab.icon}
size={24}
color={isActive ? "#0066FF" : "#000000"}
/>
)}
{/* 消息角标 */}
{isMessageTab && unreadCount > 0 && (
<View style={styles.badge}>
<Text style={styles.badgeText}>
{unreadCount > 99 ? "99+" : unreadCount}
</Text>
</View>
)}
{/* 动态红点角标 */}
{isMomentTab && hasNewMoment && !isActive && (
<View style={styles.dotBadge} />
)}
</View>
<Text
style={[
styles.tabLabel,
isActive && styles.tabLabelActive
]}
>
{tab.label}
</Text>
</TouchableOpacity>
);
})}
</View>
{/* 顶部细线 */}
<View style={styles.topBorder} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#EFEFF4", // 与QQ类似的浅灰色背景
},
// 聊天布局
messageRow: {
paddingHorizontal: 12,
marginVertical: 8,
flexDirection: "row",
alignItems: "flex-end",
},
left: {
justifyContent: "flex-start",
},
right: {
justifyContent: "flex-end",
},
avatarContainer: {
width: 40,
height: 40,
borderRadius: 6,
marginRight: 10,
backgroundColor: "#E0E0E0",
justifyContent: "center",
alignItems: "center",
},
avatar: {
width: 36,
height: 36,
borderRadius: 4,
backgroundColor: "#C0C0C0",
},
bubble: {
maxWidth: "70%",
paddingVertical: 10,
paddingHorizontal: 12,
borderRadius: 8,
},
myBubble: {
backgroundColor: "#4EA0FF",
borderTopRightRadius: 0,
},
otherBubble: {
backgroundColor: "#FFFFFF",
borderTopLeftRadius: 0,
},
msgText: {
color: "#333333",
fontSize: 16,
},
myMsgText: {
color: "#FFFFFF",
},
timeText: {
fontSize: 12,
color: "#999999",
marginTop: 4,
alignSelf: "flex-end",
},
// TabBar样式
tabBarContainer: {
position: "absolute",
left: 0,
right: 0,
bottom: 0,
height: Platform.OS === "ios" ? 85 : 60,
overflow: "hidden",
},
whiteOverlay: {
...StyleSheet.absoluteFillObject,
// 关键参数调整:稍微增加不透明度,让白色更明显
backgroundColor: "rgba(249, 249, 249, 0.95)",
},
topBorder: {
position: "absolute",
top: 0,
left: 0,
right: 0,
height: 0.5,
backgroundColor: "rgba(0, 0, 0, 0.1)", // 顶部细线
},
tabs: {
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-around",
paddingBottom: Platform.OS === "ios" ? 20 : 0,
},
tabItem: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
iconContainer: {
width: 24,
height: 24,
justifyContent: "center",
alignItems: "center",
marginBottom: 4,
position: "relative",
},
tabLabel: {
fontSize: 10,
color: "#000000",
fontWeight: "500",
},
tabLabelActive: {
color: "#0066FF",
},
badge: {
position: "absolute",
top: -6,
right: -12,
minWidth: 18,
height: 18,
borderRadius: 9,
backgroundColor: "#FF3B30",
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 4,
borderWidth: 2,
borderColor: "#FAFAFA", // 与TabBar背景色保持一致
},
badgeText: {
color: "#FFFFFF",
fontSize: 10,
fontWeight: "bold",
},
dotBadge: {
position: "absolute",
top: -4,
right: -6,
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: "#FF3B30",
borderWidth: 1.5,
borderColor: "#FAFAFA", // 与TabBar背景色保持一致
},
});