个人中心是 App 的"门面"之一,用户在这里可以看到自己的基本信息、快速访问常用功能。这篇文章来实现一个简洁实用的个人中心页面。个人中心的设计看似简单,但实际上涉及到很多细节,比如数据展示、导航逻辑、样式协调等。

页面结构分析
打开任何一个 App 的个人中心,基本都遵循这个套路:
- 顶部 - 用户信息卡片(头像、昵称、状态)
- 中间 - 统计数据展示(收藏数、浏览数等)
- 下面 - 功能菜单列表
我们的 Steam 资讯 App 也按这个思路来。不过因为这是一个资讯类 App,不涉及真正的用户登录系统,所以用户信息部分就用一个默认的 Steam 头像和昵称来展示。
设计思路 - 即使没有真实用户数据,也要把页面框架搭好。这样后续接入真实登录系统时,只需要替换数据源就行,不用改页面结构。
从全局状态获取数据
个人中心需要展示用户的收藏数量和浏览历史数量,这些数据存在全局状态里。先从全局状态中获取必要的数据:
tsx
const {navigate, favorites, history} = useApp();
这里获取了三个东西:
navigate- 导航函数,用于跳转到其他页面favorites- 收藏列表数组,直接用.length就能拿到收藏数量history- 浏览历史数组,同样用.length拿到浏览数量
为什么用 Hook? 用
useApp这个自定义 Hook 来获取全局状态,内部用的是 React Context。好处是任何组件都能方便地访问和修改全局状态,不用一层层传 props。这样代码更简洁,也更容易维护。
用户信息卡片
先来实现顶部的用户信息区域。这是个人中心最重要的视觉元素,需要展示用户的头像、昵称和在线状态。
先看卡片的结构:
tsx
<View style={styles.userCard}>
<Image
source={{uri: 'https://avatars.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg'}}
style={styles.avatar}
/>
<View style={styles.userInfo}>
<Text style={styles.userName}>Steam 用户</Text>
<Text style={styles.userStatus}>🟢 在线</Text>
</View>
</View>
这里用了一个 Steam 的默认头像 URL。实际项目中,如果接入了 Steam 登录,这里应该显示用户真实的头像和昵称。
头像处理 - 用
Image组件加载网络图片。如果用户没有网络连接,可以加一个本地的默认头像作为备选。
然后看样式设计:
tsx
userCard: {
flexDirection: 'row',
alignItems: 'center',
padding: 20,
backgroundColor: '#1b2838'
},
avatar: {width: 64, height: 64, borderRadius: 32},
userInfo: {marginLeft: 16},
userName: {fontSize: 18, fontWeight: 'bold', color: '#fff'},
userStatus: {fontSize: 14, color: '#8f98a0', marginTop: 4},
样式设计的关键点:
- 头像 - 用 64x64 的圆形,
borderRadius: 32让它变成圆的 - 用户名 - 18 号字体加粗,白色,视觉上最突出
- 在线状态 - 用绿色圆点 emoji + 灰色文字,表示用户在线
- 布局 -
flexDirection: 'row'让头像和文字信息水平排列,alignItems: 'center'让它们垂直居中对齐
Flexbox 布局 - 这是 React Native 里最常用的布局方式。
flexDirection: 'row'表示水平排列,alignItems: 'center'表示垂直居中。这两个属性组合起来就能实现很多常见的布局。
统计数据展示
用户信息下面是三个统计数字:收藏数、浏览数、游戏数。这些数字能让用户一眼看到自己在 App 中的活跃度。
先看统计区域的结构:
tsx
<View style={styles.stats}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{favorites.length}</Text>
<Text style={styles.statLabel}>收藏</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{history.length}</Text>
<Text style={styles.statLabel}>浏览</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>0</Text>
<Text style={styles.statLabel}>游戏</Text>
</View>
</View>
三个 statItem 平分整行宽度,每个里面是数字在上、标签在下的布局。
为什么"游戏"数量写死是 0? 因为这个 App 没有接入 Steam 账号系统,拿不到用户真正拥有的游戏数量。如果后续要做登录功能,这里可以改成动态数据。
然后看样式部分:
tsx
stats: {
flexDirection: 'row',
backgroundColor: '#1b2838',
borderTopWidth: 1,
borderTopColor: '#2a475e'
},
statItem: {flex: 1, alignItems: 'center', paddingVertical: 16},
statNumber: {fontSize: 24, fontWeight: 'bold', color: '#66c0f4'},
statLabel: {fontSize: 12, color: '#8f98a0', marginTop: 4},
样式设计的考虑:
- 平分宽度 -
flex: 1让三个statItem平分父容器宽度 - 数字颜色 - 用 Steam 的蓝色
#66c0f4,比较醒目,能吸引用户注意 - 标签颜色 - 用灰色
#8f98a0,不抢数字的风头 - 分割线 - 顶部加了一条分割线
borderTopWidth: 1,把统计区域和用户信息卡片在视觉上分开
视觉层级 - 通过颜色和大小的对比,让数字成为视觉焦点,标签作为辅助说明。这样用户一眼就能看到关键信息。
功能菜单列表
菜单列表是个人中心的核心部分,用户通过这里进入各个功能页面。先看菜单的结构:
tsx
<View style={styles.menu}>
<ListItem icon="❤️" title="我的收藏" rightText={`${favorites.length}`} onPress={() => navigate('favorites')} />
<ListItem icon="📜" title="浏览历史" rightText={`${history.length}`} onPress={() => navigate('history')} />
<ListItem icon="🔔" title="消息通知" onPress={() => navigate('notifications')} />
<ListItem icon="⚙️" title="设置" onPress={() => navigate('settings')} />
<ListItem icon="❓" title="帮助中心" onPress={() => navigate('help')} />
<ListItem icon="ℹ️" title="关于我们" onPress={() => navigate('about')} />
</View>
这里用了一个通用的 ListItem 组件,每个菜单项都是一个 ListItem。这样做的好处是:
- 代码复用 - 不用每个菜单项都写一遍样式
- 统一风格 - 所有菜单项的样式保持一致
- 易于维护 - 想改样式只需要改
ListItem组件
组件设计 - 通过提取通用组件,可以大幅减少重复代码。如果后续要添加新的菜单项,只需要再加一行
ListItem就行。
ListItem 组件实现
既然用到了 ListItem,来看看它是怎么实现的。这是一个通用的列表项组件,可以在很多地方复用。
首先定义 Props 接口:
tsx
interface ListItemProps {
title: string;
subtitle?: string;
icon?: string;
rightText?: string;
onPress?: () => void;
showArrow?: boolean;
}
Props 定义用 TypeScript 接口,这样在使用组件时 IDE 会有智能提示,也能在编译时发现类型错误。
title- 必传,菜单项的标题subtitle- 可选,副标题或描述文字icon- 可选,左侧图标rightText- 可选,右侧显示的文字(比如数量)onPress- 可选,点击回调函数showArrow- 可选,是否显示右侧箭头,默认为 true
然后看组件的实现:
tsx
export const ListItem = ({title, subtitle, icon, rightText, onPress, showArrow = true}: ListItemProps) => (
<TouchableOpacity style={styles.item} onPress={onPress} disabled={!onPress}>
{icon && <Text style={styles.icon}>{icon}</Text>}
<View style={styles.content}>
<Text style={styles.title}>{title}</Text>
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
</View>
{rightText && <Text style={styles.rightText}>{rightText}</Text>}
{showArrow && onPress && <Text style={styles.arrow}>›</Text>}
</TouchableOpacity>
);
组件实现的关键点:
- 条件渲染 -
{icon && <Text>...}这种写法,只有当icon有值时才渲染图标。subtitle、rightText同理 - 禁用状态 -
disabled={!onPress}表示如果没传onPress,这个按钮就不可点击。这样可以用ListItem来展示纯信息,不一定非要能点 - 箭头显示逻辑 -
{showArrow && onPress && ...}只有当showArrow为 true 且有onPress时才显示箭头。纯展示的项不需要箭头
灵活设计 - 通过这些可选属性和条件渲染,
ListItem组件可以适应多种场景。既能用作可点击的菜单项,也能用作纯展示的信息项。
然后看样式部分:
tsx
item: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
backgroundColor: '#1b2838',
borderBottomWidth: 1,
borderBottomColor: '#2a475e',
},
icon: {fontSize: 20, marginRight: 12},
content: {flex: 1},
title: {fontSize: 16, color: '#fff'},
subtitle: {fontSize: 13, color: '#8f98a0', marginTop: 2},
rightText: {fontSize: 14, color: '#8f98a0', marginRight: 8},
arrow: {fontSize: 20, color: '#8f98a0'},
样式设计的要点:
- 布局 -
flexDirection: 'row'让各个元素水平排列,alignItems: 'center'让它们垂直居中 - 内容占位 -
content设置flex: 1让它占据剩余空间,这样rightText和箭头会被挤到右边 - 分割线 - 每个菜单项底部加一条分割线,让列表看起来更清晰
- 颜色 - 标题用白色,副标题和右侧文字用灰色,形成视觉层级
页面的整体结构
个人中心页面的整体布局分为三层:
tsx
return (
<View style={styles.container}>
<Header title="我的" />
<ScrollView style={styles.content}>
{/* 用户卡片 */}
{/* 统计数据 */}
{/* 菜单列表 */}
</ScrollView>
<TabBar />
</View>
);
页面结构很清晰:
- Header - 顶部导航栏,显示"我的"标题
- ScrollView - 内容区域,包含用户卡片、统计数据和菜单列表
- TabBar - 底部导航栏,方便用户切换到其他页面
为什么用 ScrollView? 用
ScrollView包裹内容区域是个好习惯,即使现在内容不多,以后加功能时也不用担心超出屏幕。而且如果内容确实超出屏幕,用户可以滚动查看。
配色方案
个人中心页面的样式采用 Steam 的深色主题。先看容器和基本样式:
tsx
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#171a21'},
content: {flex: 1},
userCard: {flexDirection: 'row', alignItems: 'center', padding: 20, backgroundColor: '#1b2838'},
avatar: {width: 64, height: 64, borderRadius: 32},
userInfo: {marginLeft: 16},
userName: {fontSize: 18, fontWeight: 'bold', color: '#fff'},
userStatus: {fontSize: 14, color: '#8f98a0', marginTop: 4},
然后是统计区域的样式:
tsx
stats: {flexDirection: 'row', backgroundColor: '#1b2838', borderTopWidth: 1, borderTopColor: '#2a475e'},
statItem: {flex: 1, alignItems: 'center', paddingVertical: 16},
statNumber: {fontSize: 24, fontWeight: 'bold', color: '#66c0f4'},
statLabel: {fontSize: 12, color: '#8f98a0', marginTop: 4},
menu: {marginTop: 16},
});
配色说明:
#171a21- 最深的背景色,用于页面底色#1b2838- 卡片背景色,比底色稍浅#2a475e- 分割线颜色,用于区分不同区域#66c0f4- Steam 标志蓝,用于强调数字#8f98a0- 灰色,用于次要文字#fff- 白色,用于主要文字
配色统一 - 保持配色统一,整个 App 看起来才协调。这些颜色都是 Steam 官方的配色,用户会感到熟悉。
导航逻辑
点击菜单项时调用 navigate 函数跳转到对应页面。先看菜单项的点击处理:
tsx
<ListItem icon="❤️" title="我的收藏" rightText={`${favorites.length}`} onPress={() => navigate('favorites')} />
<ListItem icon="📜" title="浏览历史" rightText={`${history.length}`} onPress={() => navigate('history')} />
<ListItem icon="⚙️" title="设置" onPress={() => navigate('settings')} />
每个菜单项都有一个 onPress 回调,点击时调用 navigate() 函数。
导航方式 -
navigate函数在AppContext里定义,它会更新currentScreen状态,然后 App 根组件根据这个状态渲染对应的页面。这种方式比 React Navigation 简单,适合页面不多的小型 App。如果页面多了、导航逻辑复杂了,还是建议用 React Navigation。
数据绑定
个人中心的关键是要实时显示用户的数据。比如收藏数量和浏览历史数量需要从全局状态中获取:
tsx
<ListItem icon="❤️" title="我的收藏" rightText={`${favorites.length}`} onPress={() => navigate('favorites')} />
<ListItem icon="📜" title="浏览历史" rightText={`${history.length}`} onPress={() => navigate('history')} />
以及统计区域的数字:
tsx
<Text style={styles.statNumber}>{favorites.length}</Text>
<Text style={styles.statNumber}>{history.length}</Text>
实时更新 - 因为
favorites和history都来自全局状态,当用户在其他页面添加收藏或浏览游戏时,这些数字会自动更新。用户返回个人中心时,就能看到最新的数据。
小结
个人中心页面展示了如何设计一个简洁实用的用户中心:
- 信息展示 - 用户卡片展示基本信息,统计区域展示关键数据
- 功能导航 - 菜单列表提供快速访问各个功能的入口
- 组件复用 - 通过
ListItem组件实现代码复用和风格统一 - 数据绑定 - 从全局状态获取数据,实现实时更新
- 配色协调 - 使用 Steam 的配色方案,保持视觉统一
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net