OpenHarmony 5.0Release 开发的在线音乐应用卡片

前言

OpenHarmony 社区最新发布了 5.0 Release 版本,在多媒体播放能力上有所加强,另外卡片的功能支持也更加丰富,对于在线音乐播放类应用,卡片是一个必不可少的功能,因此,本文在 OpenHarmony 5.0Release (API12)版本上开发了在线音乐应用的卡片。

功能

预览图 2*4:

卡片功能包括:

1、展示并实时刷新播放信息:歌曲名称、歌手、播放状态、歌曲封面图(网络)

2、播放控制:播放模式、上一首、播放/暂停、下一首、收藏

实现

本功能在集成测试仓的在线音乐播放应用基础上开发: 在线音乐播放应用 。开发卡片功能包含如下几个部分:

1、创建卡片工程

右键基础工程 Entry,创建卡片工程:

根据提示创建完成后,生成卡片工程文件:

1)Ability

2)布局

3)配置

并且在工程原配置文件中增加了 Ability 标签:

bash 复制代码
"extensionAbilities": [
      {
        "name": "EntryFormAbility",
        "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
        "label": "$string:EntryFormAbility_label",
        "description": "$string:EntryFormAbility_desc",
        "type": "form",
        "metadata": [
          {
            "name": "ohos.extension.form",
            "resource": "$profile:form_config"
          }
        ]
      }
    ],

2、卡片 UI 布局

WidgeCard 布局代码:

scss 复制代码
build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Row() {
      }
      .width(this.FULL_PERCENT)
      .height(this.FULL_PERCENT)
      .displayPriority(this.DISPLAY_PRIORITY_ONE)
      .backgroundImage('memory://' + this.imgName)
      .backgroundImageSize(ImageSize.Cover)

      Column()
        .width(this.FULL_PERCENT)
        .height(this.FULL_PERCENT)
        .backgroundColor(Color.White)
        .opacity(0.9)

      Row() {
        Column() {
          Row()
            .width(128)
            .height(128)
            .borderRadius(64)
            .margin(8)
            .shadow({ radius: 2, color: Color.Gray })
            .backgroundImage('memory://' + this.imgName)
            .backgroundImageSize(ImageSize.Cover)
            .onClick(() => {
              postCardAction(this, {
                action: this.ACTION_TYPE,
                abilityName: this.ABILITY_NAME,
              });
            })
        }
        .justifyContent(FlexAlign.Center)
        .height(this.FULL_PERCENT)

        Column() {
          Row() {
            Column() {
              Text(this.audioItem.title)
                .fontSize(32)
                .fontColor($r('app.color.information_title_font'))
                .fontWeight(FontWeight.Medium)
                .maxLines(this.MAX_LINES_VALUE)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
              Text(this.audioItem.artist)
                .fontSize(24)
                .fontColor($r('app.color.item_text_font'))
                .fontWeight(FontWeight.Regular)
                .margin({ top: 4 })
                .maxLines(this.MAX_LINES_VALUE)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
            .justifyContent(FlexAlign.SpaceEvenly)
            .alignItems(HorizontalAlign.Start)
            .margin({ left: 8, right: 8 })
            .width('80%')
            .onClick(() => {
              postCardAction(this, {
                action: this.ACTION_TYPE,
                abilityName: this.ABILITY_NAME,
              });
            })

            Image(this.audioItem.isFavor ? $r('app.media.heart_fill') : $r('app.media.heart'))
              .width(48)
              .height(48)
              .onClick(() => {
                this.onSetFavor()
              })
          }
          .alignItems(VerticalAlign.Top)
          .justifyContent(FlexAlign.SpaceBetween)
          .width(this.FULL_PERCENT)

          Row() {
            Image(this.playMode === PLAY_MODE.REPEAT ? $r('app.media.repeat') : (this.playMode === PLAY_MODE.REPEAT1 ? $r('app.media.repeat_1') : $r('app.media.shuffle')))
              .width(64)
              .height(64)
              .padding(4)
              .onClick(() => {
                this.setPlayMode();
              })

            Image($r('app.media.backward_end_fill'))
              .width(64)
              .height(64)
              .padding(4)
              .onClick(() => {
                this.onPreviousClick()
              })
            if (this.isPlaying === ServerConstants.PLAYER_STATE_PLAYING) {
              Image($r('app.media.pause'))
                .width(64)
                .height(64)
                .padding(8)
                .onClick(() => {
                  this.onPauseClick();
                })
                .onAppear(() => {
                  if (this.audioItem.id !== '') {
                    if (this.downloadImgId === this.audioItem.id) {
                      return;
                    }
                    this.downloadImgId = this.audioItem.id;
                    postCardAction(this, {
                      action: 'message',
                      params: {
                        info: ServerConstants.SONG_IMAGE_URL + this.audioItem.id
                      }
                    });
                  }
                })
            } else {
              Image($r('app.media.play_fill'))
                .width(64)
                .height(64)
                .padding(4)
                .onClick(() => {
                  this.onPlayClick();
                })
            }

            Image($r('app.media.forward_end_fill'))
              .width(64)
              .height(64)
              .padding(4)
              .onClick(() => {
                this.onNextClick()
              })

          }
          .justifyContent(FlexAlign.SpaceBetween)
          .width(this.FULL_PERCENT)
        }
        .width('60%')
        .height(this.FULL_PERCENT)
        .justifyContent(FlexAlign.SpaceEvenly)
      }
      .width(this.FULL_PERCENT)
      .justifyContent(FlexAlign.SpaceEvenly)
      .displayPriority(this.DISPLAY_PRIORITY_TWO)
    }
  }

预览效果:

3、卡片内容更新

WidgetCard 通过 LocalStorageProp 绑定需要刷新的数据:

less 复制代码
  @LocalStorageProp('formId') formId: string = '';
  @LocalStorageProp('audioItem') audioItem: AudioData = new AudioData('未播放', '--', '')
  @LocalStorageProp('isPlaying') isPlaying: string = ServerConstants.PLAYER_STATE_UNKNOWN;
  @LocalStorageProp('imgName') imgName: string = '';
  @LocalStorageProp('playMode') playMode: number = PLAY_MODE.REPEAT;

在播放后台通过 formProvider.updataForm 更新卡片内容:

typescript 复制代码
  updateCard() {
    let formData: Record<string, AudioData| string> = {
      'audioItem': this.item,
      'isPlaying': this.state,
    };
    let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData);
    formProvider.updateForm(this.formId, formMsg).then(() => {
      console.info('FormAbility updateForm success.');
    }).catch((error: Base.BusinessError) => {
      console.info(`Operation updateForm failed. Cause: ${JSON.stringify(error)}`);
    })
  }
DD一下: 欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。
erlang 复制代码
`欢迎大家关注公众号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......

4、卡片功能事件

通过三种 Action 事件方式实现卡片的三种功能:

1、Router:点击图片或歌曲名跳转到应用主页面:

kotlin 复制代码
postCardAction(this, {
                action: 'router',
                abilityName: this.ABILITY_NAME,
              });

2、Call:点击播放/暂停、上一首下一首等,调用后台播放功能:

发送消息:

javascript 复制代码
  onPlayClick(): void {
    postCardAction(this, {
      'action': 'call',
      'abilityName': this.ABILITY_NAME,
      'params': {
        'method': 'play'
      }
    });
    console.info(TAG, 'postCardAction onPlayClick');
  }

后台消息处理:

kotlin 复制代码
try {
      this.callee.on('play', (data: rpc.MessageSequence) => {
        aPlayerManager.resume();
        return null;
      });

3、通过 Message 消息通知 EntryFormAbility 下载封面的网络图片并显示:

发送消息,将图片 url 发给 EntryFormAbility 的 OnFormEvent:

kotlin 复制代码
       postCardAction(this, {
                      action: 'message',
                      params: {
                        info: ServerConstants.SONG_IMAGE_URL + this.audioItem.id
                      }
                    });

下载图片并通知 UI 加载:

typescript 复制代码
let httpRequest = http.createHttp()
    httpRequest.request(params.info, (err, data) => {
      if (!err && data.responseCode == http.ResponseCode.OK) {
        let imgFile = fs.openSync(tmpFile, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
        fs.write(imgFile.fd, data.result as ArrayBuffer).then((writeLen: number) => {
          console.info(TAG, "write data to file succeed and size is:" + writeLen);
        }).catch((err: Base.BusinessError) => {
          console.error(TAG, "write data to file failed with error message: " + err.message + ", error code: " + err.code);
        }).finally(() => {
          fs.closeSync(imgFile);
        });

        console.info(TAG, 'ArkTSCard download complete: %{public}s', tmpFile);
        let file: fileFs.File;
        let fileInfo: Record<string, string | number> = {};
        try {
          file = fs.openSync(tmpFile);
          fileInfo[fileName] = file.fd;
        } catch (e) {
          console.error(TAG, `openSync failed: ${JSON.stringify(e as Base.BusinessError)}`);
        }

        class FormDataClass {
          imgName: string = fileName;
          formImages: object = fileInfo;
        }

        let formData = new FormDataClass();
        let formInfo = formBindingData.createFormBindingData(formData);
        formProvider.updateForm(formId, formInfo).then(() => {
          console.info(TAG, '%{public}s', 'FormAbility updateForm success.');
        }).catch((error: Base.BusinessError) => {
          console.error(TAG, `FormAbility updateForm failed: ${JSON.stringify(error)}`);
        });
      } else {
        console.error(TAG, `ArkTSCard download task failed. Cause: ${JSON.stringify(err)}`);
      }
      httpRequest.destroy();
    })

效果

总结

当前卡片的基本功能都可以实现,但是仍有些不足,比如:不支持图片 url 自动加载;不支持点击效果 ClickEffect;卡片对于动画的支持比较弱,不能实现长时间的动画效果;卡片进程不能成为后台任务,在生命周期没有活动时只能存活 5 秒,加载网络图片超时即失败等。

相关推荐
冯志浩2 分钟前
HarmonyOS - 嵌套类属性状态管理装饰器:ObservedV2 和 Trace
harmonyos·掘金·金石计划
塞尔维亚大汉2 小时前
【鸿蒙南向开发】OpenHarmony小型系统内核(LiteOS-A)【文件系统】下
物联网·嵌入式·harmonyos
iOS阿玮4 小时前
别等了,今天是Xcode15时代的最后一天。
前端·app·apple
Jewel1054 小时前
如何配置Telegram Mini-App?
前端·vue.js·app
智驾4 小时前
什么是鸿蒙南向开发?什么是北向开发?
harmonyos·南向开发·北向开发
周胡杰4 小时前
weibo_har鸿蒙微博分享,单例二次封装,鸿蒙微博,微博登录
华为·harmonyos
塞尔维亚大汉5 小时前
【鸿蒙南向开发】OpenHarmony小型系统内核(LiteOS-A)【扩展组件】下
物联网·嵌入式·harmonyos
智驾6 小时前
Openharmony 和 HarmonyOS 区别?
harmonyos·openharmony
别说我什么都不会7 小时前
【仓颉三方库】工具类——zip4cj & zlib4cj
harmonyos
高木的小天才18 小时前
鸿蒙中的并发线程间通信、线程间通信对象
前端·华为·typescript·harmonyos