React Native鸿蒙跨平台双横向筛选条采用多个水平 ScrollView 组合,使用 FlatList 并通过 ListHeaderComponent 衔接统计文案
本文分析了一个基于React Native构建的附近的人发现应用,详细解析了其核心架构设计。应用采用多维度用户数据模型,实现基于地理位置、在线状态和共同兴趣的智能筛选与排序系统,并设计了丰富的状态可视化展示界面。文章还探讨了该应用在鸿蒙OS平台的跨端适配方案,包括组件映射策略和关键技术适配点。该案例为位置社交类应用提供了典型的技术参考,展示了如何在保持核心功能的同时实现跨平台兼容。
概述
本文分析的是一个基于React Native构建的附近的人发现应用,集成了地理位置筛选、实时状态监测、个性化推荐等核心功能。该应用采用了多维度用户画像、智能筛选算法和实时交互设计,展现了位置社交类应用的典型技术架构。在鸿蒙OS的跨端适配场景中,这种涉及地理位置和实时状态的应用具有重要的技术参考价值。
核心架构设计深度解析
地理位置数据模型设计
应用定义了完整的附近用户数据结构:
type NearbyUser = {
id: string;
name: string;
age: number;
distance: number; // 距离(公里)
lastSeen: string; // 最后在线时间
bio: string;
commonInterests: string[];
isOnline: boolean;
isFavorite: boolean;
};
这种数据结构设计体现了位置社交的核心维度:基础信息(姓名、年龄)、地理位置(距离、最后在线)、个人特征(简介、兴趣)和状态标记(在线状态、收藏状态)。distance字段实现了基于公里的距离量化,为后续的筛选和排序提供了数据基础。
在鸿蒙ArkUI体系中,接口定义保持了相同的结构:
interface NearbyUser {
id: string;
name: string;
age: number;
distance: number;
lastSeen: string;
bio: string;
commonInterests: string[];
isOnline: boolean;
isFavorite: boolean;
}
多维度筛选与排序系统
应用实现了复杂的筛选和排序算法:
// 根据筛选条件过滤用户
const filteredUsers = users.filter(user => {
if (distanceFilter === '1km' && user.distance > 1) return false;
if (distanceFilter === '2km' && user.distance > 2) return false;
if (distanceFilter === '5km' && user.distance > 5) return false;
if (onlineFilter === 'online' && !user.isOnline) return false;
if (onlineFilter === 'offline' && user.isOnline) return false;
return true;
});
// 根据排序方式排序
const sortedUsers = [...filteredUsers].sort((a, b) => {
if (sortBy === 'distance') {
return a.distance - b.distance; // 距离升序
} else if (sortBy === 'lastSeen') {
return a.lastSeen.localeCompare(b.lastSeen); // 时间比较
} else { // commonInterest
return b.commonInterests.length - a.commonInterests.length; // 兴趣数量降序
}
});
这种处理模式采用了链式操作:先filter筛选再sort排序。展开运算符[…filteredUsers]确保了排序操作不会影响原始数据。多种排序策略提供了灵活的展示选项。
鸿蒙的实现采用计算属性模式:
@State users: NearbyUser[] = [];
@State distanceFilter: string = 'all';
@State onlineFilter: string = 'all';
@State sortBy: string = 'distance';
// 筛选后的用户
get filteredUsers(): NearbyUser[] {
return this.users.filter(user => {
if (this.distanceFilter === '1km' && user.distance > 1) return false;
if (this.distanceFilter === '2km' && user.distance > 2) return false;
if (this.distanceFilter === '5km' && user.distance > 5) return false;
if (this.onlineFilter === 'online' && !user.isOnline) return false;
if (this.onlineFilter === 'offline' && user.isOnline) return false;
return true;
});
}
// 排序后的用户
get sortedUsers(): NearbyUser[] {
return [...this.filteredUsers].sort((a, b) => {
if (this.sortBy === 'distance') {
return a.distance - b.distance;
} else if (this.sortBy === 'lastSeen') {
return a.lastSeen.localeCompare(b.lastSeen);
} else {
return b.commonInterests.length - a.commonInterests.length;
}
});
}
状态可视化展示系统
NearbyUserCard组件实现了丰富的状态展示:
const NearbyUserCard = ({ user, onSendMessage, onAddFriend, onToggleFavorite }) => {
return (
<View style={styles.userCard}>
<View style={styles.userHeader}>
<View style={styles.userAvatar}>
<Text style={styles.avatarText}>{ICONS.user}</Text>
</View>
<View style={styles.userInfo}>
<View style={styles.userDetails}>
<Text style={styles.userName}>{user.name}, {user.age}</Text>
<View style={styles.statusContainer}>
<View style={[styles.statusDot, user.isOnline ? styles.onlineStatus : styles.offlineStatus]} />
<Text style={styles.statusText}>{user.isOnline ? '在线' : '离线'}</Text>
</View>
</View>
<View style={styles.distanceContainer}>
<Text style={styles.locationIcon}>{ICONS.location}</Text>
<Text style={styles.distanceText}>{user.distance} km</Text>
</View>
</View>
</View>
{/* 更多内容 */}
</View>
);
};
这种设计采用了视觉化状态指示器:圆形状态点通过条件样式显示不同颜色(在线绿色、离线灰色),距离信息结合定位图标提供直观的地理感知。slice(0,3)限制了兴趣标签的显示数量,保持了界面的整洁性。
鸿蒙的实现需要将样式逻辑转换为声明式结构:
@Component
struct NearbyUserCard {
@Prop user: NearbyUser;
build() {
Column() {
Row() {
// 头像
Column() { Text('👤') }
// 用户信息
Column() {
Text(`${this.user.name}, ${this.user.age}`)
// 状态指示器
Row() {
Circle()
.width(8).height(8)
.fill(this.user.isOnline ? Color.Green : Color.Gray)
Text(this.user.isOnline ? '在线' : '离线')
}
}
// 距离信息
Row() {
Text('📍')
Text(`${this.user.distance} km`)
}
}
// 个人简介和兴趣
Text(this.user.bio)
Row() {
ForEach(this.user.commonInterests.slice(0, 3), (interest: string) => {
Text(interest)
})
}
}
}
}
跨端适配技术方案
组件映射策略
| React Native组件 | 鸿蒙ArkUI组件 | 关键适配点 |
|---|---|---|
| FlatList | List | 列表实现和性能优化 |
| TouchableOpacity | Button | 交互反馈机制不同 |
| ScrollView | Scroll | 滚动行为一致 |
| View | Column/Row/Stack | 布局系统转换 |
样式系统转换
// React Native
statusDot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 4,
},
onlineStatus: {
backgroundColor: '#10b981',
},
// 鸿蒙
Circle()
.width(8).height(8)
.fill(Color.Green)
状态管理迁移
// React Native
const [users, setUsers] = useState<NearbyUser[]>([]);
const [distanceFilter, setDistanceFilter] = useState('all');
// 鸿蒙
@State users: NearbyUser[] = [];
@State distanceFilter: string = 'all';
性能优化与最佳实践
列表渲染优化
配置keyExtractor提升列表性能:
<FlatList
data={sortedUsers}
keyExtractor={item => item.id}
renderItem={({ item }) => <NearbyUserCard user={item} />}
/>
筛选计算优化
使用useMemo避免重复计算:
const sortedUsers = useMemo(() => {
const filtered = users.filter(user => {
// 筛选逻辑
});
return [...filtered].sort((a, b) => {
// 排序逻辑
});
}, [users, distanceFilter, onlineFilter, sortBy]);
架构与数据建模的语义边界
这段代码以 NearbyUser 类型为中心构建页面语义,字段拆分清晰地覆盖了人设展示(name、age、bio)、活跃状态(isOnline、lastSeen)、社交意向(isFavorite)与关系契合度(commonInterests)。这种“展示模型 + 交互标志”的组合非常适合跨端桥接:鸿蒙端只需遵循同一数据契约即可在 ArkUI 层稳定渲染与分发事件。建议将 lastSeen 以数值时间戳或 ISO 字符串存储,展示层再格式化为相对时间,避免当前字符串比较造成排序不稳定;距离则统一以米/千米做单位换算,确保端上格式一致。
组件栈与交互语义
NearbyUserCard 承载了用户基本信息、在线状态、距离、兴趣标签与三类操作按钮(收藏/消息/添加),通过 onSendMessage、onAddFriend、onToggleFavorite 明确把“动作”的意义抽象为事件,而不是和样式或平台能力耦合。FilterOption 将筛选项抽象为“可选标签 + 选中态”,上层只传递 isSelected 与 onPress,使得筛选 UI 能自由替换为鸿蒙端的 ArkUI 组件而不改变业务语义。页面组件 NearbyPeopleApp 则组合统计区、筛选区与列表区,把“附近的人”这个场景分解为三层:数据统计、筛选状态机、列表渲染,这正是跨端结构化的合理落点。
筛选与排序的轻量状态机
页面维护了 distanceFilter、onlineFilter 与 sortBy 三个维度,使用 filter → sort 的顺序派生出最终渲染列表。派生过程建议用 useMemo 包裹,依赖 users、distanceFilter、onlineFilter、sortBy,减少不必要重算与重渲染,尤其在鸿蒙端桥接场景下能明显降低跨层事件与 UI 刷新次数。收藏切换当前用 setUsers(users.map(…)) 实现不可变更新,为防止闭包旧值导致快速连点时状态回跳,采用函数式更新 setUsers(prev => prev.map(…)) 更稳妥;事件处理器也应通过 useCallback 固定引用,从而减少子组件的无效更新。
列表与虚拟化的跨端调优
附近用户列表使用 FlatList 并通过 ListHeaderComponent 衔接统计文案,是 RN 侧组织内容的典型手段。双横向筛选条采用多个水平 ScrollView 组合,在 RN 端是合理方案;在鸿蒙端需要注意滚动物理与事件节流,在 RN 侧为列表设置合适的 initialNumToRender、windowSize、removeClippedSubviews,并在尺寸可预期时提供 getItemLayout,使渲染与回收行为更可控。ArkUI 侧的滚动与曝光埋点建议走统一抽象,避免每个列表项单独上报造成跨端事件风暴。
能力桥接与操作统一
当前消息与加好友采用 Alert 作为占位提示,生产实现建议抽象为“能力服务层”:在 RN 端定义 sendMessage(userId, payload)、addFriend(userId) 这类语义化 API;在鸿蒙端以 ArkUI 或 AbilityBridge 实现具体能力,包括权限申请、失败重试、队列与离线缓存。收藏操作作为纯本地状态更新,也应该在桥接层同时支持“服务器确认”的异步流,以便在端上做乐观更新与失败回滚,保证跨端一致的交互体验。
性能与可观测性闭环
列表 keyExtractor 稳定使用 item.id,有助于 RN 侧复用与鸿蒙端桥接的识别。交互埋点建议统一事件名与属性协议,例如 favorite_toggle、message_click、add_friend_click,并在桥接层聚合上报,减少端间差异。网络错误与权限拒绝应在能力服务层完成统一处理,页面只关心展示与重试语义,从而让跨端差异被封装起来,避免散落在组件中。
权限与隐私的工程边界
“附近的人”往往涉及定位能力与相邻用户聚合,这在鸿蒙端必须通过明确的权限申请与最小化数据原则来实现。建议把定位与刷新能力封装为“位置服务”,在 RN 层提供 refreshNearbyUsers 的语义,而在 ArkUI 侧处理权限弹窗、后台更新策略与低功耗配置。用户资料属于敏感数据,应统一通过安全存储或加密传输,并在端上提供“清理缓存/撤回授权”的一致入口。
跨端一致性的落点
把“页面语义”与“平台能力”解耦是贯穿始终的原则:上层组件只描述展示与交互意图,中层服务抽象统一输入/输出与错误语义,底层桥接实现鸿蒙的 ArkUI/Ability 能力。列表虚拟化、筛选状态机、能力服务与图标抽象这四个层面一旦建立,React Native 与鸿蒙端就能在同一语义下演进,平台差异被边界化在可控的实现细节里,既可维护又可测试。
完整代码code:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';
// 图标库
const ICONS = {
user: '👤',
location: '📍',
message: '💬',
add: '➕',
heart: '❤️',
phone: '📞',
filter: '🔍',
refresh: '🔄',
};
const { width } = Dimensions.get('window');
// 用户类型
type NearbyUser = {
id: string;
name: string;
age: number;
distance: number; // 距离(公里)
lastSeen: string; // 最后在线时间
bio: string;
commonInterests: string[];
isOnline: boolean;
isFavorite: boolean;
};
// 附近用户卡片组件
const NearbyUserCard = ({
user,
onSendMessage,
onAddFriend,
onToggleFavorite
}: {
user: NearbyUser;
onSendMessage: (id: string) => void;
onAddFriend: (id: string) => void;
onToggleFavorite: (id: string) => void;
}) => {
return (
<View style={styles.userCard}>
<View style={styles.userHeader}>
<View style={styles.userAvatar}>
<Text style={styles.avatarText}>{ICONS.user}</Text>
</View>
<View style={styles.userInfo}>
<View style={styles.userDetails}>
<Text style={styles.userName}>{user.name}, {user.age}</Text>
<View style={styles.statusContainer}>
<View style={[styles.statusDot, user.isOnline ? styles.onlineStatus : styles.offlineStatus]} />
<Text style={styles.statusText}>{user.isOnline ? '在线' : '离线'}</Text>
</View>
</View>
<View style={styles.distanceContainer}>
<Text style={styles.locationIcon}>{ICONS.location}</Text>
<Text style={styles.distanceText}>{user.distance} km</Text>
</View>
</View>
</View>
<Text style={styles.userBio}>{user.bio}</Text>
<View style={styles.interestsContainer}>
{user.commonInterests.slice(0, 3).map((interest, index) => (
<View key={index} style={styles.interestTag}>
<Text style={styles.interestText}>{interest}</Text>
</View>
))}
</View>
<View style={styles.lastSeenContainer}>
<Text style={styles.lastSeenText}>最后在线: {user.lastSeen}</Text>
</View>
<View style={styles.cardActions}>
<TouchableOpacity
style={[styles.actionButton, styles.favoriteButton, user.isFavorite && styles.favoritedButton]}
onPress={() => onToggleFavorite(user.id)}
>
<Text style={[styles.actionText, user.isFavorite && styles.favoritedText]}>
{user.isFavorite ? ICONS.heart : '🤍'} 收藏
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, styles.messageButton]}
onPress={() => onSendMessage(user.id)}
>
<Text style={styles.actionText}>{ICONS.message} 消息</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, styles.addButton]}
onPress={() => onAddFriend(user.id)}
>
<Text style={styles.actionText}>{ICONS.add} 添加</Text>
</TouchableOpacity>
</View>
</View>
);
};
// 筛选选项组件
const FilterOption = ({ label, value, isSelected, onPress }: {
label: string;
value: string;
isSelected: boolean;
onPress: () => void
}) => {
return (
<TouchableOpacity
style={[styles.filterOption, isSelected && styles.selectedFilterOption]}
onPress={onPress}
>
<Text style={[styles.filterText, isSelected && styles.selectedFilterText]}>{label}</Text>
</TouchableOpacity>
);
};
// 主页面组件
const NearbyPeopleApp: React.FC = () => {
const [users, setUsers] = useState<NearbyUser[]>([
{
id: '1',
name: '张小明',
age: 28,
distance: 0.5,
lastSeen: '刚刚',
bio: '喜欢摄影和旅行,周末经常去户外徒步',
commonInterests: ['摄影', '旅行', '户外'],
isOnline: true,
isFavorite: false
},
{
id: '2',
name: '李小红',
age: 25,
distance: 1.2,
lastSeen: '5分钟前',
bio: '健身爱好者,专注于瑜伽和普拉提',
commonInterests: ['健身', '瑜伽', '健康生活'],
isOnline: true,
isFavorite: true
},
{
id: '3',
name: '王大伟',
age: 32,
distance: 2.8,
lastSeen: '1小时前',
bio: '程序员,喜欢科技和游戏',
commonInterests: ['编程', '游戏', '科技'],
isOnline: false,
isFavorite: false
},
{
id: '4',
name: '陈美丽',
age: 27,
distance: 0.8,
lastSeen: '刚刚',
bio: '美食博主,擅长制作各种甜品',
commonInterests: ['美食', '烘焙', '烹饪'],
isOnline: true,
isFavorite: false
},
{
id: '5',
name: '刘小华',
age: 30,
distance: 3.5,
lastSeen: '2小时前',
bio: '音乐老师,喜欢吉他和创作',
commonInterests: ['音乐', '吉他', '创作'],
isOnline: false,
isFavorite: true
}
]);
const [distanceFilter, setDistanceFilter] = useState('all'); // all, 1km, 2km, 5km
const [onlineFilter, setOnlineFilter] = useState('all'); // all, online, offline
const [sortBy, setSortBy] = useState('distance'); // distance, lastSeen, commonInterest
const handleSendMessage = (id: string) => {
Alert.alert('发送消息', `向 ${users.find(u => u.id === id)?.name} 发送消息`);
};
const handleAddFriend = (id: string) => {
Alert.alert('添加好友', `已向 ${users.find(u => u.id === id)?.name} 发送好友请求`);
};
const handleToggleFavorite = (id: string) => {
setUsers(users.map(user =>
user.id === id
? { ...user, isFavorite: !user.isFavorite }
: user
));
};
const refreshNearbyUsers = () => {
Alert.alert('刷新', '正在为您查找附近的人...');
};
// 根据筛选条件过滤用户
const filteredUsers = users.filter(user => {
if (distanceFilter === '1km' && user.distance > 1) return false;
if (distanceFilter === '2km' && user.distance > 2) return false;
if (distanceFilter === '5km' && user.distance > 5) return false;
if (onlineFilter === 'online' && !user.isOnline) return false;
if (onlineFilter === 'offline' && user.isOnline) return false;
return true;
});
// 根据排序方式排序
const sortedUsers = [...filteredUsers].sort((a, b) => {
if (sortBy === 'distance') {
return a.distance - b.distance;
} else if (sortBy === 'lastSeen') {
// 简化处理,实际应用中需要更复杂的比较逻辑
return a.lastSeen.localeCompare(b.lastSeen);
} else { // commonInterest
return b.commonInterests.length - a.commonInterests.length;
}
});
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>附近的人</Text>
<TouchableOpacity style={styles.refreshButton} onPress={refreshNearbyUsers}>
<Text style={styles.refreshText}>{ICONS.refresh} 刷新</Text>
</TouchableOpacity>
</View>
{/* 统计信息 */}
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{users.length}</Text>
<Text style={styles.statLabel}>附近用户</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>
{users.filter(u => u.isOnline).length}
</Text>
<Text style={styles.statLabel}>在线用户</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>
{users.filter(u => u.isFavorite).length}
</Text>
<Text style={styles.statLabel}>收藏用户</Text>
</View>
</View>
{/* 筛选选项 */}
<View style={styles.filtersContainer}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.filtersRow}>
<Text style={styles.filterCategory}>距离:</Text>
<FilterOption
label="全部"
value="all"
isSelected={distanceFilter === 'all'}
onPress={() => setDistanceFilter('all')}
/>
<FilterOption
label="1km内"
value="1km"
isSelected={distanceFilter === '1km'}
onPress={() => setDistanceFilter('1km')}
/>
<FilterOption
label="2km内"
value="2km"
isSelected={distanceFilter === '2km'}
onPress={() => setDistanceFilter('2km')}
/>
<FilterOption
label="5km内"
value="5km"
isSelected={distanceFilter === '5km'}
onPress={() => setDistanceFilter('5km')}
/>
</View>
</ScrollView>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.filtersRow}>
<Text style={styles.filterCategory}>状态:</Text>
<FilterOption
label="全部"
value="all"
isSelected={onlineFilter === 'all'}
onPress={() => setOnlineFilter('all')}
/>
<FilterOption
label="在线"
value="online"
isSelected={onlineFilter === 'online'}
onPress={() => setOnlineFilter('online')}
/>
<FilterOption
label="离线"
value="offline"
isSelected={onlineFilter === 'offline'}
onPress={() => setOnlineFilter('offline')}
/>
</View>
</ScrollView>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.filtersRow}>
<Text style={styles.filterCategory}>排序:</Text>
<FilterOption
label="距离"
value="distance"
isSelected={sortBy === 'distance'}
onPress={() => setSortBy('distance')}
/>
<FilterOption
label="活跃度"
value="lastSeen"
isSelected={sortBy === 'lastSeen'}
onPress={() => setSortBy('lastSeen')}
/>
<FilterOption
label="兴趣"
value="commonInterest"
isSelected={sortBy === 'commonInterest'}
onPress={() => setSortBy('commonInterest')}
/>
</View>
</ScrollView>
</View>
{/* 附近用户列表 */}
<FlatList
data={sortedUsers}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<NearbyUserCard
user={item}
onSendMessage={handleSendMessage}
onAddFriend={handleAddFriend}
onToggleFavorite={handleToggleFavorite}
/>
)}
style={styles.usersList}
showsVerticalScrollIndicator={false}
ListHeaderComponent={
<Text style={styles.sectionTitle}>附近的人 ({sortedUsers.length})</Text>
}
/>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.location}</Text>
<Text style={styles.navText}>附近</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.message}</Text>
<Text style={styles.navText}>消息</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>{ICONS.user}</Text>
<Text style={styles.navText}>我的</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.filter}</Text>
<Text style={styles.navText}>筛选</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8fafc',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
refreshButton: {
backgroundColor: '#3b82f6',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
refreshText: {
color: '#ffffff',
fontSize: 14,
fontWeight: '500',
},
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 16,
backgroundColor: '#ffffff',
marginBottom: 16,
},
statItem: {
alignItems: 'center',
},
statNumber: {
fontSize: 20,
fontWeight: 'bold',
color: '#3b82f6',
},
statLabel: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
filtersContainer: {
backgroundColor: '#ffffff',
paddingHorizontal: 16,
marginBottom: 16,
},
filtersRow: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 8,
},
filterCategory: {
fontSize: 14,
color: '#1e293b',
marginRight: 8,
fontWeight: '500',
},
filterOption: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
backgroundColor: '#f1f5f9',
marginRight: 8,
},
selectedFilterOption: {
backgroundColor: '#3b82f6',
},
filterText: {
fontSize: 12,
color: '#64748b',
},
selectedFilterText: {
color: '#ffffff',
},
usersList: {
flex: 1,
paddingHorizontal: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginVertical: 12,
},
userCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
userHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
userAvatar: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#dbeafe',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
avatarText: {
fontSize: 24,
color: '#3b82f6',
},
userInfo: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
userDetails: {
flex: 1,
},
userName: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
statusContainer: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 4,
},
statusDot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 4,
},
onlineStatus: {
backgroundColor: '#10b981',
},
offlineStatus: {
backgroundColor: '#94a3b8',
},
statusText: {
fontSize: 12,
color: '#64748b',
},
distanceContainer: {
flexDirection: 'row',
alignItems: 'center',
},
locationIcon: {
fontSize: 14,
color: '#64748b',
marginRight: 4,
},
distanceText: {
fontSize: 12,
color: '#64748b',
},
userBio: {
fontSize: 14,
color: '#334155',
lineHeight: 20,
marginBottom: 12,
},
interestsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: 8,
},
interestTag: {
backgroundColor: '#f1f5f9',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
marginBottom: 4,
},
interestText: {
fontSize: 12,
color: '#475569',
},
lastSeenContainer: {
marginBottom: 12,
},
lastSeenText: {
fontSize: 12,
color: '#64748b',
},
cardActions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
actionButton: {
flex: 1,
padding: 10,
borderRadius: 8,
alignItems: 'center',
marginHorizontal: 2,
},
favoriteButton: {
backgroundColor: '#f1f5f9',
},
favoritedButton: {
backgroundColor: '#fee2e2',
},
messageButton: {
backgroundColor: '#e0f2fe',
},
addButton: {
backgroundColor: '#3b82f6',
},
actionText: {
fontSize: 12,
fontWeight: '500',
color: '#64748b',
},
favoritedText: {
color: '#ef4444',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingBottom: 2,
borderBottomWidth: 2,
borderBottomColor: '#3b82f6',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#3b82f6',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#3b82f6',
fontWeight: '500',
},
});
export default NearbyPeopleApp;
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐





所有评论(0)