HarmonyOS NEXT-给应用添加通知和提醒

一、知识学习

1 通知介绍

通知旨在让用户以合适的方式及时获得有用的新消息,帮助用户高效地处理任务。应用可以通过通知接口发送通知消息,用户可以通过通知栏查看通知内容,也可以点击通知来打开应用,通知主要有以下使用场景:

  • 显示接收到的短消息、即时消息等。
  • 显示应用的推送消息,如广告、版本更新等。
  • 显示当前正在进行的事件,如下载等。

通知表现形式

通知会在不同场景以不同形式提示用户,例如通知在状态栏上显示为图标、在通知栏上会显示通知详细信息。重要的信息还可以使用横幅通知,浮动在界面顶部显示。

通知结构

下面以基础的文本通知为例,介绍通知的基本结构。

1. 通知小图标:表示通知的功能与类型。

2. 通知名称:应用名称或功能名称。

3. 时间:发送通知的时间,系统默认显示。

4. 展开箭头:点击标题区,展开被折叠的内容和按钮。若无折叠的内容和按钮,不显示此箭头。

5. 内容标题:描述简明概要。

6. 内容详情:描述具体内容或详情。

2 创建通知

本节将介绍几种常见类型通知的创建,在创建通知前需要先导入notificationManager模块,该模块提供通知管理的能力,包括发布、取消发布通知,创建、获取、移除通知通道等能力。

import { notificationManager } from '@kit.NotificationKit';

发布基础类型通知

基础类型通知主要应用于发送短信息、提示信息、广告推送等,支持普通文本类型、长文本类型、多行文本类型,可以通过ContentType指定通知的内容类型。下面以普通文本类型为例来介绍基础通知的发布,其它基础类型您可以查阅API。

  • 发布普通文本类型通知,需要设置ContentType类型为ContentType.NOTIFICATION_CONTENT_BASIC_TEXT。
@Entry 
@Component 
struct NotificationDemo { 
  publishNotification() { 
    let notificationRequest: notificationManager.NotificationRequest = { // 描述通知的请求 
      id: 1, // 通知ID  
      content: { // 通知内容 
        notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知 
        normal: { // 基本类型通知内容 
          title: '通知内容标题', 
          text: '通知内容详情' 
        } 
      } 
    } 
    notificationManager.publish(notificationRequest).then(() => { // 发布通知 
      console.info('publish success'); 
    }).catch((err: Error) => { 
      console.error(`publish failed,message is ${err}`); 
    }); 
  } 
 
  build() { 
    Column() { 
      Button('发送通知') 
        .onClick(() => { 
          this.publishNotification() 
        }) 
    } 
    .width('100%') 
  } 
}

效果图如下:

发布进度类型通知

进度条通知也是常见的通知类型,主要应用于文件下载、事务处理进度显示。目前系统模板仅支持进度条模板,效果示意如下图所示:

在发布进度类型通知前需要查询系统是否支持进度条模板。

notificationManager.isSupportTemplate('downloadTemplate').then(isSupport => {
  if (!isSupport) {
    promptAction.showToast({
      message: $r('app.string.invalid_button_toast')
    })
  }
  this.isSupport = isSupport;
});

构造进度条模板,name字段当前需要固定配置为downloadTemplate。

let template: notificationManager.NotificationTemplate = { 
  name: 'downloadTemplate', 
  data: { 
    progressValue: progress, // 当前进度值 
    progressMaxValue: 100 // 最大进度值 
  } 
} 
 
let notificationRequest: notificationManager.NotificationRequest = { 
  id: 1, 
  content: { 
    notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, 
    normal: { 
      title: '文件下载:music.mp4', 
      text: 'senTemplate', 
      additionalText: '60%' 
    } 
  }, 
   template: template   
} 
// 发布通知 
notificationManager.publish(notificationRequest).then(() => { 
  console.info(`publish success`); 
}).catch((err: Error) => { 
  console.error(`publish failed,message is ${err}`); 
})

更新通知

在发出通知后,使用您之前使用的相同通知ID,再次调用notificationManager.publish来实现通知的更新。如果之前的通知是关闭的,将会创建新通知。

移除通知

  • 通过通知ID和通知标签取消已发布的通知。

    notificationManager.cancel(notificationId)

  • 取消所有已发布的通知。

    notificationManager.cancelAll()

3 设置通知通道

通过通知通道,您可让通知有不同的表现形式,比如社交类型的通知是横幅显示的,并且有提示音,而一般的通知则不会横幅显示,您可以使用slotType来实现,设置slotType为SlotType.SOCIAL_COMMUNICATION,表示为社交类型通知。示例代码如下:

notificationManager.addSlot(notificationManager.SlotType.SOCIAL_COMMUNICATION).then(() => {
  console.info("addSlot success");
}).catch((err: Base.BusinessError) => {
  console.error(`addSlot fail: ${JSON.stringify(err)}`);
});

效果图如下:

通知通道类型主要有以下几种:

  • SlotType.SOCIAL_COMMUNICATION:社交类型,状态栏中显示通知图标,有横幅和提示音。
  • SlotType.SERVICE_INFORMATION:服务类型,状态栏中显示通知图标,没有横幅但有提示音。
  • SlotType.CONTENT_INFORMATION:内容类型,状态栏中显示通知图标,但没有横幅或提示音。
  • SlotType.OTHER_TYPES:其它类型,状态栏中不显示通知图标,且没有横幅或提示音。

4 创建通知组

将不同类型的通知分为不同的组,以便用户可以更好的管理他们。当同组的通知有多条的时候,会自动折叠起来,避免通知比较多的时候,通知界面比较杂乱,例如当通知栏里有聊天消息通知和商品推荐通知时,我们只需要通过设置字段groupName,就可以对通知进行分组,给groupName设置不同的值可以将通知分为不同的组。

您可以使用groupName来指定通知组来实现,示例代码如下:

let notifyId = 0; 
 
let chatRequest: notificationManager.NotificationRequest = {  
  id: notifyId++, 
  groupName:'ChatGroup', 
  content: { 
    //... 
   } 
 }; 
 
let productRequest: notificationManager.NotificationRequest = {  
  id: notifyId++, 
  groupName: 'ProductGroup', 
  content: { 
    //... 
   } 
 };

5 为通知添加行为意图

WantAgent提供了封装行为意图的能力,这里所说的行为意图主要是指拉起指定的应用组件及发布公共事件等能力。给通知添加行为意图后,点击通知后可以拉起指定的UIAbility或者发布公共事件,您可以按照以下步骤来实现:

  1. 导入模块。
import { notificationManager } from '@kit.NotificationKit'; 
import { wantAgent, WantAgent } from '@kit.AbilityKit';
  1. 创建WantAgentInfo信息。

    场景一:拉起UIAbility。

    let wantAgentInfo = { 
      wants: [ 
        { 
          bundleName: "com.example.notification", 
          abilityName: "EntryAbility" 
        } 
      ], 
      operationType: wantAgent.OperationType.START_ABILITY, 
      requestCode: 100 
    }

    场景二:发布公共事件。

    let wantAgentInfo = { 
      wants: [ 
        { 
          action: 'event_name', // 设置事件名 
          parameters: {}, 
        } 
      ], 
      operationType: wantAgent.OperationType.SEND_COMMON_EVENT, 
      requestCode: 100, 
      wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], 
    }

  2. 创建WantAgent对象。
    let wantAgentObj = null;  
    wantAgent.getWantAgent(wantAgentInfo) 
      .then((data) => { 
        wantAgentObj = data; 
      }) 
      .catch((err: Error) => { 
        console.error(`get wantAgent failed because ${JSON.stringify(err)}`); 
      })

  3. 构造NotificationRequest对象。
    let notificationRequest: notificationManager.NotificationRequest = {
      id: 1, 
      content: { 
        notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, 
        normal: { 
          title: "通知标题", 
          text: "通知内容" 
        } 
      }, 
      wantAgent: wantAgentObj 
    };
  4. 发布WantAgent通知。
    notificationManager.publish(notificationRequest).then(() => { // 发布通知
      console.info("publish success"); 
    }).catch((err: Error) => { 
      console.error(`publish failed, code is ${err.code}, message is ${err.message}`); 
    });  

参考链接

二、综合实战

案例截图

代码结构

CommonConstants.ets

/**
 * Common constants for all features.
 */
export default class CommonConstants {

  /**
   * The width or height is full.
   */
  static readonly FULL_LENGTH: string = '100%';

  /**
   * The width of title.
   */
  static readonly TITLE_WIDTH: string = '86.7%';

  /**
   * The width of download card.
   */
  static readonly CARD_WIDTH: string = '93.3%';

  /**
   * The weight of image.
   */
  static readonly IMAGE_WEIGHT: number = 1;

  /**
   * The weight of card content.
   */
  static readonly CARD_CONTENT_WEIGHT: number = 5;

  /**
   * The width of download content.
   */
  static readonly CARD_CONTENT_WIDTH: string = '70%';

  /**
   * The width of download image.
   */
  static readonly CARD_IMAGE_WIDTH: string = '30%';

  /**
   * The name of download file.
   */
  static readonly DOWNLOAD_FILE: string = '1653067.mp4';

  /**
   * The size of download file.
   */
  static readonly FILE_SIZE: string = '25.01MB';

  /**
   * Font weight of Large.
   */
  static readonly FONT_WEIGHT_LAGER: number = 500;

  /**
   * Font opacity.
   */
  static readonly FONT_OPACITY: number = 0.6;

  /**
   * Progress total.
   */
  static readonly PROGRESS_TOTAL: number = 100;

  /**
   * Progress Update speed.
   */
  static readonly PROGRESS_SPEED: number = 2;

  /**
   * Progress Update frequency.
   */
  static readonly UPDATE_FREQUENCY: number = 1000;

  /**
   * Notification id.
   */
  static readonly NOTIFICATION_ID: number = 1000;
}

/**
 * Download status.
 */
export enum DOWNLOAD_STATUS {
  INITIAL,
  DOWNLOADING,
  PAUSE,
  FINISHED
}

Logger.ets

import { hilog } from '@kit.PerformanceAnalysisKit';

class Logger {
  private domain: number;
  private prefix: string;
  private format: string = '%{public}s, %{public}s';

  /**
   * constructor.
   *
   * @param Prefix identifies the log tag.
   * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
   */
  constructor(prefix: string = 'MyApp', domain: number = 0xFF00) {
    this.prefix = prefix;
    this.domain = domain;
  }

  debug(...args: string[]): void {
    hilog.debug(this.domain, this.prefix, this.format, args);
  }

  info(...args: string[]): void {
    hilog.info(this.domain, this.prefix, this.format, args);
  }

  warn(...args: string[]): void {
    hilog.warn(this.domain, this.prefix, this.format, args);
  }

  error(...args: string[]): void {
    hilog.error(this.domain, this.prefix, this.format, args);
  }
}

export default new Logger('DownLoadNotification', 0xFF00);

NotificationUtil.ets

import { wantAgent } from '@kit.AbilityKit';
import { notificationManager } from '@kit.NotificationKit';
import CommonConstants from '../constants/CommonConstants';
import Logger from '../utils/Logger';

/**
 * Obtains the WantAgent of an application.
 *
 * @returns WantAgent of an application.
 */
export function createWantAgent(bundleName: string, abilityName: string): Promise<object> {
  let wantAgentInfo = {
    wants: [
      {
        bundleName: bundleName,
        abilityName: abilityName
      }
    ],
    operationType: wantAgent.OperationType.START_ABILITY,
    requestCode: 0,
    wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]
  } as wantAgent.WantAgentInfo;
  return wantAgent.getWantAgent(wantAgentInfo);
}

/**
 * Publish notification.
 *
 * @param progress Download progress
 * @param title Notification title.
 * @param wantAgentObj The want of application.
 */
export function publishNotification(progress: number, title: string, wantAgentObj: object) {
  let template:notificationManager.NotificationTemplate = {
    name: 'downloadTemplate',
    data: {
      title:  `${title}`,
      fileName:  `${title}:${CommonConstants.DOWNLOAD_FILE}`,
      progressValue: progress,
      progressMaxValue: CommonConstants.PROGRESS_TOTAL,
      isProgressIndeterminate: false
    }
  };
  let notificationRequest: notificationManager.NotificationRequest = {
    id: CommonConstants.NOTIFICATION_ID,
    notificationSlotType: notificationManager.SlotType.CONTENT_INFORMATION,
    // Construct a progress bar template. The name field must be set to downloadTemplate.
    template: template,
    content: {
      notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: `${title}:${CommonConstants.DOWNLOAD_FILE}`,
        text: ' ',
        additionalText: `${progress}%`
      }
    },
    wantAgent: wantAgentObj
  };
  notificationManager.publish(notificationRequest).catch((err: Error) => {
    Logger.error(`[ANS] publish failed,message is ${err}`);
  });
}

/**
 * open notification permission
 */
export function openNotificationPermission() {
  notificationManager.requestEnableNotification().then(() => {
    Logger.info('Enable notification success');
  }).catch((err:Error) => {
    Logger.error('Enable notification failed because ' + JSON.stringify(err));
  });
}

ResourseUtil.ets

import Logger from '../utils/Logger';

/**
 * Obtains the resource string.
 *
 * @param resource Resource path.
 * @returns Promise instance of a string.
 */
export async function getStringByRes(resource: Resource, component: Object): Promise<string> {
  if (!resource) {
    Logger.error('getStringByRes resource is invalid');
    return '';
  }
  let string = await getContext(component).resourceManager.getStringValue(resource.id);
  return string;
}

MainPage.ets

import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { notificationManager } from '@kit.NotificationKit';
import { createWantAgent, publishNotification, openNotificationPermission } from '../common/utils/NotificationUtil';
import { getStringByRes } from '../common/utils/ResourseUtil';
import Logger from '../common/utils/Logger';
import CommonConstants, { DOWNLOAD_STATUS } from '../common/constants/CommonConstants';

@Entry
@Component
struct MainPage {
  @State downloadStatus: number = DOWNLOAD_STATUS.INITIAL;
  @State downloadProgress: number = 0;
  private context = getContext(this) as common.UIAbilityContext;
  private isSupport: boolean = true;
  private notificationTitle: string = '';
  private wantAgentObj: object = new Object();
  private interval: number = -1;

  aboutToAppear() {
    openNotificationPermission();
    let bundleName = this.context.abilityInfo.bundleName;
    let abilityName = this.context.abilityInfo.name;
    createWantAgent(bundleName, abilityName).then(want => {
      this.wantAgentObj = want;
    }).catch((err: Error) => {
      Logger.error(`getWantAgent fail, err: ${JSON.stringify(err)}`);
    });
    notificationManager.isSupportTemplate('downloadTemplate').then(isSupport => {
      if (!isSupport) {
        promptAction.showToast({
          message: $r('app.string.invalid_button_toast')
        })
      }
      this.isSupport = isSupport;
    });
  }

  onBackPress() {
    this.cancel();
  }

  build() {
    Column() {
      Text($r('app.string.title'))
        .fontSize($r('app.float.title_font_size'))
        .fontWeight(CommonConstants.FONT_WEIGHT_LAGER)
        .width(CommonConstants.TITLE_WIDTH)
        .textAlign(TextAlign.Start)
        .margin({
          top: $r('app.float.title_margin_top'),
          bottom: $r('app.float.title_margin_top')
        })
      Row() {
        Column() {
          Image($r('app.media.ic_image'))
            .objectFit(ImageFit.Fill)
            .width($r('app.float.card_image_length'))
            .height($r('app.float.card_image_length'))
        }
        .layoutWeight(CommonConstants.IMAGE_WEIGHT)
        .height(CommonConstants.FULL_LENGTH)
        .alignItems(HorizontalAlign.Start)

        Column() {
          Row() {
            Text(CommonConstants.DOWNLOAD_FILE)
              .fontSize($r('app.float.name_font_size'))
              .textAlign(TextAlign.Center)
              .fontWeight(CommonConstants.FONT_WEIGHT_LAGER)
              .lineHeight($r('app.float.name_font_height'))
            Text(`${this.downloadProgress}%`)
              .fontSize($r('app.float.normal_font_size'))
              .lineHeight($r('app.float.name_font_height'))
              .opacity(CommonConstants.FONT_OPACITY)
          }
          .justifyContent(FlexAlign.SpaceBetween)
          .width(CommonConstants.FULL_LENGTH)

          Progress({
            value: this.downloadProgress,
            total: CommonConstants.PROGRESS_TOTAL
          })
            .width(CommonConstants.FULL_LENGTH)
          Row() {
            Text(CommonConstants.FILE_SIZE)
              .fontSize($r('app.float.normal_font_size'))
              .lineHeight($r('app.float.name_font_height'))
              .opacity(CommonConstants.FONT_OPACITY)
            if (this.downloadStatus === DOWNLOAD_STATUS.INITIAL) {
              this.customButton($r('app.string.button_download'), (): Promise<void> => this.start())
            } else if (this.downloadStatus === DOWNLOAD_STATUS.DOWNLOADING) {
              Row() {
                this.cancelButton()
                this.customButton($r('app.string.button_pause'), (): Promise<void> => this.pause())
              }
            } else if (this.downloadStatus === DOWNLOAD_STATUS.PAUSE) {
              Row() {
                this.cancelButton()
                this.customButton($r('app.string.button_resume'), (): Promise<void> => this.resume())
              }
            } else {
              this.customButton($r('app.string.button_finish'), (): void => this.open())
            }
          }
          .width(CommonConstants.FULL_LENGTH)
          .justifyContent(FlexAlign.SpaceBetween)
        }
        .layoutWeight(CommonConstants.CARD_CONTENT_WEIGHT)
        .height(CommonConstants.FULL_LENGTH)
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .width(CommonConstants.CARD_WIDTH)
      .height($r('app.float.card_height'))
      .backgroundColor(Color.White)
      .borderRadius($r('app.float.card_border_radius'))
      .justifyContent(FlexAlign.SpaceBetween)
      .padding($r('app.float.card_padding'))
    }
    .width(CommonConstants.FULL_LENGTH)
    .height(CommonConstants.FULL_LENGTH)
    .backgroundColor($r('app.color.index_background_color'))
  }

  /**
   * Start the timer and send notification.
   */
  download() {
    this.interval = setInterval(async () => {
      if (this.downloadProgress === CommonConstants.PROGRESS_TOTAL) {
        this.notificationTitle = await getStringByRes($r('app.string.notification_title_finish'), this);
        this.downloadStatus = DOWNLOAD_STATUS.FINISHED;
        clearInterval(this.interval);
      } else {
        this.downloadProgress += CommonConstants.PROGRESS_SPEED;
      }
      if (this.isSupport) {
        publishNotification(this.downloadProgress, this.notificationTitle, this.wantAgentObj);
      }
    }, CommonConstants.UPDATE_FREQUENCY);
  }

  /**
   * Click to download.
   */
  async start() {
    this.notificationTitle = await getStringByRes($r('app.string.notification_title_download'), this);
    this.downloadStatus = DOWNLOAD_STATUS.DOWNLOADING;
    this.downloadProgress = 0;
    this.download();
  }

  /**
   * Click pause.
   */
  async pause() {
    this.notificationTitle = await getStringByRes($r('app.string.notification_title_pause'), this);
    clearInterval(this.interval);
    this.downloadStatus = DOWNLOAD_STATUS.PAUSE;
    if (this.isSupport) {
      publishNotification(this.downloadProgress, this.notificationTitle, this.wantAgentObj);
    }
  }

  /**
   * Click resume.
   */
  async resume() {
    this.notificationTitle = await getStringByRes($r('app.string.notification_title_download'), this);
    this.download();
    this.downloadStatus = DOWNLOAD_STATUS.DOWNLOADING;
  }

  /**
   * Click cancel.
   */
  async cancel() {
    this.downloadProgress = 0;
    clearInterval(this.interval);
    this.downloadStatus = DOWNLOAD_STATUS.INITIAL;
    notificationManager.cancel(CommonConstants.NOTIFICATION_ID);
  }

  /**
   * Open file
   */
  open() {
    promptAction.showToast({
      message: $r('app.string.invalid_button_toast')
    })
  }

  @Builder
  customButton(textResource: Resource, click: Function = () => {
  }) {
    Button(textResource)
      .backgroundColor($r('app.color.button_color'))
      .buttonsStyle()
      .onClick(() => {
        click();
      })
  }

  @Builder
  cancelButton() {
    Button($r('app.string.button_cancel'))
      .buttonsStyle()
      .backgroundColor($r('app.color.cancel_button_color'))
      .fontColor($r('app.color.button_color'))
      .margin({ right: $r('app.float.button_margin') })
      .onClick(() => {
        this.cancel();
      })
  }
}

@Extend(Button)
function buttonsStyle() {
  .constraintSize({ minWidth: $r('app.float.button_width') })
  .height($r('app.float.button_height'))
  .borderRadius($r('app.float.button_border_radius'))
  .fontSize($r('app.float.button_font_size'))
}

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值