在 Expo 应用中添加声音和振动通知

本文档旨在指导开发者如何在 Expo 应用中集成声音和振动通知。我们将探讨如何利用 expo-av 播放声音以及使用 react-native 的 Vibration API 实现振动效果,并着重解决在特定时间触发通知的问题。同时,我们也关注权限处理,这是实现通知功能的关键环节。

集成声音通知

首先,确保你已经安装了 expo-av 依赖:

npx expo install expo-av

以下代码展示了如何播放声音:

import { useEffect, useState } from "react";
import { Audio } from 'expo-av';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';

async function schedulePushNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: "Reminder!",
      body: 'It\'s time!',
      sound: 'default', // Use 'default' for the default notification sound
      data: { data: 'goes here' },
    },
    trigger: { seconds: 5, repeats: false }, // Schedule for 5 seconds from now
  });
}

const useSound = () => {
  const [sound, setSound] = useState(null);

  useEffect(() => {
    async function loadSound() {
      try {
        const { sound: soundObject } = await Audio.Sound.createAsync(
          require('./assets/notification.mp3'), // 替换为你的音频文件路径
          { shouldPlay: false } // 初始时不播放
        );
        setSound(soundObject);
        console.log('Sound loaded successfully');

        // Set audio mode to allow playing sound in silent mode (iOS)
        await Audio.setAudioModeAsync({
          playsInSilentModeIOS: true,
          staysActiveInBackground: true,
          interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
          interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,
          shouldDuckAndroid: false,
        });
      } catch (error) {
        console.error("Failed to load the sound", error);
      }
    }

    loadSound();

    return () => {
      if (sound) {
        sound.unloadAsync();
      }
    };
  }, []);

  const playSound = async () => {
    if (sound) {
      try {
        await sound.replayAsync(); // Use replayAsync to play from the beginning
        console.log('Playing Sound');
      } catch (error) {
        console.error("Failed to play the sound", error);
      }
    }
  };

  return { playSound };
};

export default function App() {
  const { playSound } = useSound();

  useEffect(() => {
    // Schedule notification
    schedulePushNotification();

    // Configure notifications
    async function configurePushNotifications() {
      const { status } = await Notifications.getPermissionsAsync();
      let finalStatus = status;

      if (finalStatus !== 'granted') {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }

      if (finalStatus !== 'granted') {
        alert('Failed to get push token for push notification!');
        return;
      }

      if (Platform.OS === 'android') {
        Notifications.setNotificationChannelAsync('default', {
          name: 'default',
          importance: Notifications.AndroidImportance.MAX,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: '#FF231F7C',
        });
      }
    }

    configurePushNotifications();
  }, []);

  useEffect(() => {
    const subscription = Notifications.addNotificationReceivedListener(notification => {
      console.log('Notification Received', notification);
      playSound();
    });

    const responseListener = Notifications.addNotificationResponseReceivedListener(response => {
      console.log('Notification Response', response);
    });

    return () => {
      subscription.remove();
      responseListener.remove();
    };
  }, [playSound]);

  return (
    // Your UI components here
    null
  );
}

关键点:

  • 使用 Audio.Sound.createAsync 加载音频文件。确保文件路径正确。
  • 使用 sound.playAsync() 播放声音。
  • 在组件卸载时,使用 sound.unloadAsync() 释放资源。
  • Notifications.setNotificationHandler 设置通知处理程序,允许播放声音。

注意: 音频文件需要放置在你的项目中,例如 assets 目录下。你需要替换 require('../assets/sonido.mp3') 为你的实际文件路径。

集成振动通知

要添加振动效果,你需要从 react-native 导入 Vibration API:

import { Vibration } from 'react-native';

然后,你可以使用 Vibration.vibrate() 方法触发振动。你可以传递一个数字(毫秒)来指定振动持续时间,或者传递一个模式数组来定义振动和暂停的序列:

import { Vibration } from 'react-native';

const vibrate = () => {
  Vibration.vibrate(); // 默认振动
  //Vibration.vibrate(1000); // 振动 1 秒
  //Vibration.vibrate([0, 500, 200, 500], true); // 自定义模式 (启动, 振动500ms, 暂停200ms, 振动500ms, 循环)
};

关键点:

  • Vibration.vibrate() 触发振动。
  • 可以传递一个数字指定振动时长。
  • 可以传递一个数组定义振动模式。

将振动集成到你的通知处理程序中:

import { useEffect } from "react";
import * as Notifications from "expo-notifications";
import { Alert, Vibration } from "react-native";
import React from "react";
import { useNavigation } from "@react-navigation/native";
import { Audio } from 'expo-av';

Notifications.setNotificationHandler({
  handleNotification: async () => {
    return {
      shouldPlaySound: true,
      shouldSetBadge: false,
      shouldShowAlert: true,
    };
  },
});

const HandleNotifications = () => {
  const navigation = useNavigation();

  useEffect(() => {
    async function configurePushNotifications() {
      const { status } = await Notifications.getPermissionsAsync();
      let finalStatus = status;
      if (finalStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }
      if (finalStatus !== "granted") {
        Alert.alert(
          "Permiso requerido",
          "Se requieren notificaciones locales para recibir alertas cuando vencen los recordatorios."
        );
        return;
      }
    }

    configurePushNotifications();
  }, []);

  useEffect(() => {
    const subscription = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        console.log("RESPONSE", response);
        const reminderId = response.notification.request.content.data.reminderId;
        if (reminderId) {
          navigation.navigate("ModifyReminder", { reminderId });
        }
      }
    );

    return () => subscription.remove();
  }, []);

  useEffect(() => {
    let soundObject = null;

    async function playSound() {
      soundObject = new Audio.Sound();
      try {
        await soundObject.loadAsync(require('../assets/sonido.mp3'));
        await soundObject.playAsync();
      } catch (error) {
        console.log(error);
      }
    }

    playSound();

    return () => {
      if (soundObject) {
        soundObject.stopAsync();
        soundObject.unloadAsync();
      }
    };
  }, []);

  useEffect(() => {
    const appFocusSubscription = Notifications.addNotificationResponseReceivedListener(() => {
      Vibration.cancel();
      Audio.setIsEnabledAsync(false);
    });

    const appBlurSubscription = Notifications.addNotificationResponseReceivedListener(() => {
      Audio.setIsEnabledAsync(true);
    });

    return () => {
      appFocusSubscription.remove();
      appBlurSubscription.remove();
    };
  }, []);

  useEffect(() => {
    const subscription = Notifications.addNotificationReceivedListener(() => {
      Vibration.vibrate([0, 500, 200, 500], true);
    });

    return () => {
      subscription.remove();
      Vibration.cancel();
    };
  }, []);


  return ;
};

export default HandleNotifications;

注意: Vibration.cancel() 可以停止振动。

处理权限

在 Android 和 iOS 上,你需要请求用户授权才能发送通知。 Expo 提供 Notifications.requestPermissionsAsync() 方法来处理权限请求。

import * as Notifications from 'expo-notifications';
import { Alert } from 'react-native';

async function configurePushNotifications() {
  const { status } = await Notifications.getPermissionsAsync();
  let finalStatus = status;

  if (finalStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }

  if (finalStatus !== 'granted') {
    Alert.alert(
      "Permiso requerido",
      "Se requieren notificaciones locales para recibir alertas cuando vencen los recordatorios."
    );
    return;
  }
}

确保在组件挂载时调用 configurePushNotifications()。

在特定时间触发通知

要实现在特定时间触发通知,你需要使用 Notifications.scheduleNotificationAsync() 方法,并设置 trigger 属性。trigger 属性可以是一个 Date 对象,也可以是一个包含 seconds 属性的对象,表示从现在开始的秒数。

import * as Notifications from 'expo-notifications';

async function schedulePushNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: "Reminder!",
      body: 'It\'s time!',
      sound: 'default',
      data: { data: 'goes here' },
    },
    trigger: { seconds: 5, repeats: false }, // Schedule for 5 seconds from now
  });
}

关键点:

  • 使用 Notifications.scheduleNotificationAsync() 安排通知。
  • trigger 属性控制通知触发时间。

总结

通过结合 expo-av 和 react-native 的 Vibration API,你可以轻松地在 Expo 应用中添加声音和振动通知。 确保正确处理权限,并使用 Notifications.scheduleNotificationAsync() 方法在特定时间触发通知。 记住,用户体验至关重要,合理使用通知可以提升应用的用户满意度。

以上就是在 Expo 应用中添加声音和振动通知的详细内容,更多请关注骃骐网【www.myinqi.com】。

相关推荐:

如何给Python Tkinter窗口添加背景图片_在Canvas画布上绘制Image

Tk/Toplevel窗口本身不支持图片背景,仅接受颜色值;必须用Canvas控件通过create_image配合anchor="nw"和place(relwidth=1, relheight=1)实现伪背景,并强引用PhotoImage防止回收。 为什么直接给 Tk 或 Toplevel 设置背景图会失败 因为 Tk 和 Toplevel 窗口本身不支持 background 属性设为图片,只认...

Python Tkinter怎么给界面添加背景音乐_集成pygame播放音频文件

Tkinter 本身不能播背景音乐,因其无内置音频功能,控件仅负责界面渲染;硬调系统命令会阻塞主线程且跨平台不一致;应使用 pygame.mixer 非阻塞播放,并在 root.mainloop() 前初始化,关闭窗口时需手动 stop 和 quit。 为什么 Tkinter 本身不能播背景音乐 Tkinter 没有内置音频播放能力,tkinter.Label 或 tkinter.Frame 这些...

如何在 Django 中正确向多对多关系的模型添加数据

本文详解 Django 中向含 ManyToManyField 字段的模型(如医生预约系统中的 Appoiment)插入数据的正确方法,重点解决因字段类型不匹配导致的保存失败问题,并提供可直接运行的视图逻辑与关键注意事项。 本文详解 django 中向含 `manytomanyfield` 字段的模型(如医生预约系统中的 appoiment)插入数据的正确方法,重点解决因字段类型不匹配导致的保存失...

Django 中正确向多对多字段添加数据的完整教程

本文详解 Django 模型中 ManyToManyField 的正确使用方式,重点解决新手在创建预约记录时无法将医生关联到 Appointment 实例的问题,涵盖模型定义、视图逻辑、数据绑定及常见错误规避。 本文详解 django 模型中 `manytomanyfield` 的正确使用方式,重点解决新手在创建预约记录时无法将医生关联到 appointment 实例的问题,涵盖模型定义、视图逻辑...

使用 JavaScript 在表格中添加列总和行

本文将指导你如何使用 JavaScript 动态生成 HTML 表格,并在表格底部添加一行,用于显示每一列的总和。通过修改现有的表格生成函数,我们将在循环中计算每一列的总和,并在表格生成完毕后,将总和添加到表格的最后一行。 计算并显示表格列总和 首先,我们需要修改现有的 tablica() 函数,以便在生成表格的同时计算每一列的总和。我们将创建三个变量 sum1、sum2 和 sum3 来分别存储...

JavaScript:动态表格中添加求和行

本文将介绍如何使用 JavaScript 在动态生成的 HTML 表格底部添加一行,用于显示各列的总和。我们将通过修改现有的表格生成函数,在循环中累加每一列的值,并在表格生成完毕后,将这些总和值添加到表格的最后一行。该方法适用于初学者,并提供清晰的代码示例和步骤说明。 实现步骤 要实现动态表格底部添加求和行,主要分为以下几个步骤: 初始化累加变量: 在表格生成函数开始时,初始化用于存储每列总和的变...

使用 JavaScript 在表格底部添加列总和行

本文将详细介绍如何使用 JavaScript 在动态生成的 HTML 表格底部添加包含各列总和的行。通过在循环中累加每一列的值,并在表格生成后添加包含总和的新行,可以轻松实现这一功能。本文提供了清晰的代码示例和详细的步骤说明,帮助你理解并应用这一技术。 实现步骤 要在表格底部添加列总和行,我们需要在生成表格数据的循环中,同时计算每一列的总和。然后,在表格生成完毕后,将这些总和添加到新的表格行中。 ...

uni-app如何在头部添加一个按钮

在uni-app中添加头部按钮可以通过两种方法实现:1. 使用titlenview属性在配置文件中添加按钮,简单但只能添加一个按钮;2. 自定义导航栏,灵活性高但需要更多代码。这两种方法各有优缺点,选择取决于具体需求。 在uni-app中添加一个头部按钮其实是一件很简单但又充满乐趣的事情。让我们从这个问题的核心出发,探索一下如何实现它,同时分享一些在实际开发中可能遇到的挑战和解决方案。 首先,我们...

JavaScript 中使用 classList 添加 CSS 类时解决样式冲突

本文旨在解决在使用 JavaScript 的 classList 动态添加 CSS 类时,由于类添加顺序或 CSS 优先级问题导致的样式冲突。我们将探讨如何确保添加的类能够正确覆盖之前的类,并提供多种解决方案,包括显式移除冲突类和利用 CSS 的级联特性。通过学习本文,你将能够有效地管理和控制 JavaScript 中的 CSS 类,避免样式冲突,实现预期的视觉效果。 在使用 JavaScript...

PyCharm怎么添加CSS_PyCharm中CSS文件创建与关联教程

在PyCharm中添加CSS需创建.css文件并用标签引用。首先在项目static或css目录右键新建Stylesheet,命名如style.css;随后在HTML的中通过相对路径或框架语法(如Flask的url_for、Django的{% static %})引入;PyCharm提供路径提示与代码补全,提升准确性;存放位置推荐遵循框架约定,如Flask/Django的static文件夹,保持资源...