React Native for OpenHarmony 实战:Steam 资讯 App 通知设置实现

案例开源地址:https://atomgit.com/nutpi/rn_openharmony_steam

通知是 App 与用户沟通的重要方式。通过通知,App 可以告诉用户有新的游戏上架、特惠信息、或者其他重要事件。这篇文章来实现一个完整的通知设置系统,让用户能够灵活控制接收哪些通知。

通知类型的定义

首先需要定义 App 中有哪些类型的通知。不同的通知类型可以独立控制,用户可以选择只接收某些类型的通知。

先定义通知类型的接口:

tsx 复制代码
interface NotificationSettings {
  gameRelease: boolean;      // 新游戏发布
  specialOffers: boolean;    // 特惠信息
  priceDrops: boolean;       // 价格下降
  achievements: boolean;     // 成就相关
  newsUpdates: boolean;      // 新闻更新
}

这个接口定义了五种通知类型:

  • gameRelease - 新游戏发布通知,用户可以及时了解最新游戏
  • specialOffers - 特惠信息通知,用户不会错过优惠
  • priceDrops - 价格下降通知,用户关注的游戏降价时会收到提醒
  • achievements - 成就相关通知,用户完成成就时会收到通知
  • newsUpdates - 新闻更新通知,用户可以及时了解游戏新闻

灵活设计 - 通过定义多种通知类型,用户可以精细化控制通知。有些用户可能只关心特惠信息,有些用户可能只关心新游戏发布。这样的设计能满足不同用户的需求。

然后在全局状态中添加通知设置:

tsx 复制代码
interface AppState {
  notificationSettings: NotificationSettings;
  // ... 其他状态
}

通知设置页面的状态管理

通知设置页面需要从全局状态中获取当前的通知设置,然后提供 UI 让用户修改。先看状态的获取:

tsx 复制代码
export const NotificationSettingsScreen = () => {
  const {notificationSettings, updateNotificationSettings} = useApp();

这里获取了两个东西:

  • notificationSettings - 当前的通知设置对象,包含各种通知类型的开关状态
  • updateNotificationSettings - 更新通知设置的函数

为什么分离通知设置? 虽然通知设置也可以放在通用的 updateSettings 函数中,但单独提供 updateNotificationSettings 函数可以让代码更清晰。这样通知相关的逻辑都集中在一起,便于维护。

通知类型列表

通知设置页面的核心是一个通知类型列表,每个类型都有一个开关。用户可以通过开关来启用或禁用某种通知。

先看通知列表的定义:

tsx 复制代码
const notificationTypes = [
  {key: 'gameRelease', label: '新游戏发布', description: '当有新游戏发布时通知我'},
  {key: 'specialOffers', label: '特惠信息', description: '当游戏打折时通知我'},
  {key: 'priceDrops', label: '价格下降', description: '我关注的游戏降价时通知我'},
  {key: 'achievements', label: '成就相关', description: '完成成就时通知我'},
  {key: 'newsUpdates', label: '新闻更新', description: '有新的游戏新闻时通知我'},
];

这个数组定义了所有的通知类型。每个类型包含三个信息:

  • key - 用于在状态中查找对应的开关
  • label - 通知类型的名称,显示给用户
  • description - 通知类型的描述,帮助用户理解这个通知是干什么的

可维护性 - 通过数组来定义通知类型,如果要添加新的通知类型,只需要在数组中添加一项就行。不需要修改 UI 代码。

然后用 map 遍历这个数组来生成 UI:

tsx 复制代码
<View style={styles.notificationList}>
  {notificationTypes.map(type => (
    <View key={type.key} style={styles.notificationItem}>
      <View style={styles.notificationInfo}>
        <Text style={styles.notificationLabel}>{type.label}</Text>
        <Text style={styles.notificationDescription}>{type.description}</Text>
      </View>
      <Switch
        value={notificationSettings[type.key as keyof NotificationSettings]}
        onValueChange={(value) => updateNotificationSettings(type.key, value)}
        trackColor={{false: '#2a475e', true: '#66c0f4'}}
        thumbColor={notificationSettings[type.key as keyof NotificationSettings] ? '#fff' : '#8f98a0'}
      />
    </View>
  ))}
</View>

这段代码的关键点:

  • 遍历数组 - map 遍历通知类型数组,为每个类型生成一个 UI 项
  • 动态访问 - notificationSettings[type.key as keyof NotificationSettings] 根据 key 动态访问通知设置中的对应值
  • 开关绑定 - Switch 组件的 value 绑定到通知设置,onValueChange 在用户切换时更新设置

类型安全 - 用 as keyof NotificationSettings 来告诉 TypeScript 这个 key 是有效的。这样可以避免类型错误。

通知项的样式

每个通知项包含标签、描述和开关三部分。需要合理安排它们的布局和样式。

先看通知项的样式:

tsx 复制代码
notificationItem: {
  flexDirection: 'row',
  justifyContent: 'space-between',
  alignItems: 'center',
  paddingVertical: 12,
  paddingHorizontal: 16,
  borderBottomWidth: 1,
  borderBottomColor: '#2a475e',
  backgroundColor: '#1b2838',
},
notificationInfo: {flex: 1, marginRight: 12},
notificationLabel: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
notificationDescription: {fontSize: 12, color: '#8f98a0'},

样式设计的要点:

  • 布局 - flexDirection: 'row' 让信息和开关水平排列,justifyContent: 'space-between' 让它们分别靠左和靠右
  • 信息区 - flex: 1 让信息区占据剩余空间,marginRight: 12 给开关留出空间
  • 标签 - 用较大的字体和白色显示,是视觉焦点
  • 描述 - 用较小的字体和灰色显示,作为辅助说明
  • 分割线 - 每个项底部加一条分割线,让列表看起来更清晰

通知权限的请求

在实际应用中,App 需要向系统请求通知权限。用户需要在系统设置中允许 App 发送通知,App 才能真正发送通知。

先看权限请求的逻辑:

tsx 复制代码
import {PermissionsAndroid} from 'react-native';

const requestNotificationPermission = async () => {
  try {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
      {
        title: '通知权限',
        message: '我们需要您的许可来发送通知',
        buttonNeutral: '稍后再说',
        buttonNegative: '拒绝',
        buttonPositive: '允许',
      }
    );
    
    if (granted === PermissionsAndroid.RESULTS.GRANTED) {
      console.log('通知权限已获得');
    } else {
      console.log('通知权限被拒绝');
    }
  } catch (err) {
    console.warn(err);
  }
};

这段代码的关键点:

  • 权限请求 - 用 PermissionsAndroid.request() 向系统请求通知权限
  • 权限名称 - POST_NOTIFICATIONS 是 Android 13+ 中发送通知所需的权限
  • 对话框配置 - 配置权限请求对话框的标题、消息和按钮文字
  • 结果处理 - 根据用户的选择(允许或拒绝)进行相应的处理

权限管理 - 在 Android 13+ 中,App 需要明确请求通知权限。用户可以在系统设置中随时撤销权限。App 需要优雅地处理权限被拒绝的情况。

通知权限的检查

在页面加载时,需要检查 App 是否已经获得通知权限。如果没有,可以提示用户请求权限。

先看权限检查的逻辑:

tsx 复制代码
useEffect(() => {
  checkNotificationPermission();
}, []);

const checkNotificationPermission = async () => {
  try {
    const hasPermission = await PermissionsAndroid.check(
      PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
    );
    
    if (!hasPermission) {
      // 权限未获得,可以提示用户
      setPermissionGranted(false);
    } else {
      setPermissionGranted(true);
    }
  } catch (err) {
    console.warn(err);
  }
};

这段代码的逻辑:

  • 权限检查 - 用 PermissionsAndroid.check() 检查是否已获得通知权限
  • 状态更新 - 根据检查结果更新本地状态
  • 错误处理 - 用 try-catch 捕获可能的错误

权限状态 - 通过检查权限状态,App 可以决定是否显示权限请求提示。如果权限已获得,就不需要再提示。

通知权限提示

如果用户还没有授予通知权限,可以显示一个提示,引导用户请求权限。

先看权限提示的 UI:

tsx 复制代码
{!permissionGranted && (
  <View style={styles.permissionPrompt}>
    <Text style={styles.promptTitle}>启用通知</Text>
    <Text style={styles.promptDescription}>
      为了接收通知,您需要在系统设置中允许 App 发送通知
    </Text>
    <TouchableOpacity
      style={styles.promptButton}
      onPress={requestNotificationPermission}
    >
      <Text style={styles.promptButtonText}>请求权限</Text>
    </TouchableOpacity>
  </View>
)}

这个提示包含三部分:

  • 标题 - 告诉用户这是关于什么的
  • 描述 - 解释为什么需要这个权限
  • 按钮 - 用户可以点击按钮来请求权限

用户体验 - 通过友好的提示和清晰的说明,用户能理解为什么 App 需要这个权限,更容易同意授予权限。

页面的整体结构

通知设置页面的整体布局分为几部分:

tsx 复制代码
return (
  <View style={styles.container}>
    <Header title="通知设置" showBack />
    <ScrollView style={styles.content}>
      {/* 权限提示 */}
      {!permissionGranted && <PermissionPrompt />}
      
      {/* 通知类型列表 */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>通知类型</Text>
        {/* 通知项列表 */}
      </View>
      
      {/* 其他设置 */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>其他设置</Text>
        {/* 其他设置项 */}
      </View>
    </ScrollView>
    <TabBar />
  </View>
);

页面结构很清晰:

  • Header - 顶部导航栏,显示"通知设置"标题
  • 权限提示 - 如果权限未获得,显示权限请求提示
  • 通知类型列表 - 显示各种通知类型的开关
  • 其他设置 - 可以添加其他通知相关的设置
  • TabBar - 底部导航栏

分层设计 - 通过分层来组织内容,让页面看起来更清晰。用户可以快速找到想要的设置。

通知设置的持久化

通知设置需要保存到本地,这样用户的设置才能在应用关闭后保留。

先看保存通知设置的函数:

tsx 复制代码
const saveNotificationSettings = async (settings: NotificationSettings) => {
  try {
    await AsyncStorage.setItem('notificationSettings', JSON.stringify(settings));
  } catch (error) {
    console.error('Error saving notification settings:', error);
  }
};

这个函数的作用:

  • 序列化 - 用 JSON.stringify() 将设置对象转换成字符串
  • 存储 - 用 AsyncStorage.setItem() 将字符串存储到本地
  • 错误处理 - 用 try-catch 捕获可能的错误

然后看加载通知设置的函数:

tsx 复制代码
const loadNotificationSettings = async () => {
  try {
    const data = await AsyncStorage.getItem('notificationSettings');
    return data ? JSON.parse(data) : getDefaultNotificationSettings();
  } catch (error) {
    console.error('Error loading notification settings:', error);
    return getDefaultNotificationSettings();
  }
};

const getDefaultNotificationSettings = (): NotificationSettings => ({
  gameRelease: true,
  specialOffers: true,
  priceDrops: true,
  achievements: false,
  newsUpdates: true,
});

这段代码的逻辑:

  • 读取数据 - 用 AsyncStorage.getItem() 从本地读取数据
  • 反序列化 - 用 JSON.parse() 将字符串转换回对象
  • 默认值 - 如果没有保存的设置,使用默认值
  • 错误处理 - 如果读取失败,也使用默认值

默认值设计 - 通过提供合理的默认值,即使用户是第一次使用 App,也能获得良好的体验。默认值应该是大多数用户都想要的设置。

样式汇总

通知设置页面的样式采用 Steam 的深色主题。先看容器和基本样式:

tsx 复制代码
const styles = StyleSheet.create({
  container: {flex: 1, backgroundColor: '#171a21'},
  content: {flex: 1, paddingVertical: 16},
  section: {marginBottom: 24, paddingHorizontal: 16},
  sectionTitle: {fontSize: 14, fontWeight: 'bold', color: '#8f98a0', marginBottom: 12},
  notificationList: {backgroundColor: '#1b2838', borderRadius: 8, overflow: 'hidden'},
  notificationItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingVertical: 12,
    paddingHorizontal: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#2a475e',
  },
  notificationInfo: {flex: 1, marginRight: 12},
  notificationLabel: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
  notificationDescription: {fontSize: 12, color: '#8f98a0'},

然后是权限提示的样式:

tsx 复制代码
  permissionPrompt: {
    marginBottom: 24,
    paddingHorizontal: 16,
    paddingVertical: 12,
    backgroundColor: '#2a475e',
    borderRadius: 8,
    borderLeftWidth: 4,
    borderLeftColor: '#66c0f4',
  },
  promptTitle: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
  promptDescription: {fontSize: 12, color: '#8f98a0', marginBottom: 12},
  promptButton: {paddingVertical: 8, paddingHorizontal: 12, backgroundColor: '#66c0f4', borderRadius: 6},
  promptButtonText: {fontSize: 12, color: '#fff', fontWeight: '600', textAlign: 'center'},
});

配色说明:

  • #171a21 - 最深的背景色,用于页面底色
  • #1b2838 - 列表背景色
  • #2a475e - 分割线和提示框背景色
  • #66c0f4 - Steam 标志蓝,用于强调和按钮
  • #8f98a0 - 灰色,用于次要文字
  • #fff - 白色,用于主要文字

小结

通知设置页面展示了如何实现一个完整的通知系统:

  • 通知类型定义 - 定义多种通知类型,让用户能精细化控制
  • 灵活的 UI 生成 - 通过数组 + map 的方式生成 UI,方便添加新的通知类型
  • 权限管理 - 正确处理系统权限的请求和检查
  • 用户引导 - 通过友好的提示引导用户授予权限
  • 数据持久化 - 使用 AsyncStorage 保存用户的通知设置
  • 默认值设计 - 提供合理的默认值,确保良好的初始体验

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
—Qeyser2 小时前
Flutter 生命周期完全指南:从出生到死亡的全过程
前端·javascript·flutter
2501_948122632 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 帮助中心实现
javascript·react native·react.js·游戏·ecmascript·harmonyos
游戏技术分享2 小时前
【鸿蒙游戏技术分享 第76期】鸿蒙游戏拉不起支付,报错1001860056
游戏·华为·harmonyos
念念不忘 必有回响2 小时前
Vue页面布局与路由映射实战:RouterView嵌套及动态组件生成详解
前端·javascript·vue.js
冰暮流星2 小时前
javascript数据类型转换-转换为数字型
开发语言·前端·javascript
小雨下雨的雨2 小时前
Flutter鸿蒙共赢——像素的解构:沃罗诺伊点描与权重平衡的艺术
flutter·ui·华为·harmonyos·鸿蒙系统
千里马-horse2 小时前
React Native bridging 源码分析--ClassTest.cpp
javascript·c++·react native·react.js·bridging
小高0072 小时前
2026 年,只会写 div 和 css 的前端将彻底失业
前端·javascript·vue.js
www_stdio2 小时前
Git 提交AI神器:用大模型帮你写出规范的 Commit Message
前端·javascript·react.js