任务1:准备播放的音乐素材

步骤1:准备mp3歌曲资源

将准备好的歌曲资源放入 rawfile 目录下,示意图如下
image.png

步骤2:定义歌曲信息模型

定义每首歌的信息如下所示。

export default interface Song {
  id: number; // 索引
  title: string; // 标题
  author: string // 作者
  path: string; // 文件路径
}

步骤3:准备歌曲数据

//歌曲数据
const SONGS: Song[] = [
  {
    id: 1,
    title: "如风",
    author: "王菲",
    path: "如风.mp3"
  },
  {
    id: 2,
    title: "容易受伤的女人",
    author: "王菲",
    path: "容易受伤的女人.mp3"
  },
  {
    id: 3,
    title: "执迷不悔",
    author: "王菲",
    path: "执迷不悔.mp3"
  }
]

任务2:构建播放器UI界面

示意图如下
image.png

播放器UI界面分为3个部分:

  • 标题
  • 歌曲列表。单击歌曲,开始播放歌曲。
  • 控制器。显示歌曲名称、可以播放/暂停。没有播放歌曲的时候显示“点击歌曲开发播放”

页面框架结构如下:

build() {
  Column() {
    this.Header()  //标题
    this.SongList()  //歌曲列表
    this.SongShow()  //歌曲控制器
  }.height('100%')
  .width('100%')
}

步骤1:标题

@Builder Header(){
  Row() {
    Row() {
      Text('音乐播放器')
        .fontColor(Color.White).fontSize(32)
    }.margin({ left: 20 })
  }.backgroundColor(Color.Green).height('8%').width('100%')
}

步骤2:歌曲列表

@Builder SongList(){
  Column() {
    List() {
      ForEach(SONGS, (song: Song) => {
        ListItem() {
          Row() {
            Button({ type: ButtonType.Normal }) {
              Row() {
                Text(song.id + '')
                  .fontSize(32)
                Column() {
                  Text(song.title).fontSize(20).fontWeight(700)
                  Text(song.author).fontSize(14)
                }.alignItems(HorizontalAlign.Start)
                .margin({ left: 20 })
              }.justifyContent(FlexAlign.Start)
              .width('90%')
            }
            .backgroundColor(Color.White)
            .width("100%")
            .height(50)
            .margin({ top: 10 })
            .onClick(() => {
              this.playerState = '暂停';
              this.isPlaying = true;
              this.onPageShow();
              this.changeSong(song);
              this.selectedSong = song;
            })
          }
        }
      })
    }.width('100%')
  }.height('84%')
}

步骤3:歌曲控制器

@Builder SongShow(){
  Row() {
    Row() {
      if (this.selectedSong.id == -1) {
        Text('点击歌曲开始播放')
          .fontSize(20).fontColor(Color.White)
      } else {
        //左侧 70% ,显示歌曲名称
        Column() {
          Text(this.selectedSong.title)
            .fontSize(20).fontColor(Color.White)
        }.width('70%').alignItems(HorizontalAlign.Start)
        //右侧20%,显示播放、暂停控制
        Column() {
          Button({ type: ButtonType.Normal, stateEffect: true }) {
            Text(this.playerState)
              .fontSize(20).fontColor(Color.White)
          }
          .borderRadius(8)
          .height(26)
          .width(70)
          .backgroundColor(Color.Orange)
          .onClick(() => {
             this.playPause()
          })
        }.width('20%')
      }
    }.width('99%').margin({ left: 15 })
  }.backgroundColor(Color.Green).height('8%').width('100%')
}

任务3:实现音乐播放器

步骤1:定义变量

//播放器
private avPlayer: media.AVPlayer | null = null;
//是否正在播放
private isPlaying: boolean = false;
//播放/暂停 按钮的文字
@State playerState: string = '暂停'
//当前正在播放的歌曲
@State selectedSong: Song = {
  id: -1,
  title: '',
  author: '',
  path: ''
};

步骤2:点击歌曲列表开始播放歌曲

找到列表Item,设置点击事件

.onClick(() => {
  this.playerState = '暂停';
  this.isPlaying = true;
  this.onPageShow();
  this.changeSong(song);
  this.selectedSong = song;
})

调用内部函数:onPageShow()开始初始化播放器,并设置监听

async onPageShow() {
  // 创建avPlayer实例对象
  this.avPlayer = await media.createAVPlayer();
  // 创建状态机变化回调函数
  this.setAVPlayerCallback();
  console.info('播放器准备完成')
}
setAVPlayerCallback() {
  if (this.avPlayer !== null) {
    this.avPlayer.on('error', (err) => {
      console.error(TAG, `播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`);
      this.avPlayer?.reset();
    });

    this.avPlayer.on('stateChange', async (state, reason) => {
      console.info(TAG, "reason=" + JSON.stringify(reason))
      switch (state) {
        case 'initialized':
          console.info(TAG, '资源初始化完成');
          this.avPlayer?.prepare();
          break;
        case 'prepared':
          console.info(TAG, '资源准备完成');
          this.avPlayer?.play();
          break;
        case 'completed':
          console.info(TAG, '播放完成');
          this.avPlayer?.stop();
          break;
      }
    });
  }
}

然后调用changeSong()开始播放歌曲

// 以下为使用资源管理接口获取打包在HAP内的媒体资源文件并通过fdSrc属性进行播放
async changeSong(song: Song) {
  if (this.avPlayer !== null) {
    this.avPlayer?.reset()
    // 创建状态机变化回调函数
    this.setAVPlayerCallback();
    // 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
    // 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(song.path);
    // 为fdSrc赋值触发initialized状态机上报
    this.avPlayer.fdSrc = fileDescriptor;
  }
}

步骤3:播放、暂停控制

示意图如下,在底部控制器栏有一个按钮,可以控制播放、暂停。
image.png

实现代码如下

//播放/暂停
playPause(){
  if (this.avPlayer !== null && this.isPlaying == true) {
    /**
     * 如果正在播放,点击按钮
     * - 暂停播放
     * - 播放状态为false
     * - 按钮文字改为继续
     */
    this.avPlayer.pause()
    this.playerState = '继续'
    this.isPlaying = false
  } else {
    /**
     * 如果不在播放,也就是暂停、或者未开始,点击按钮
     * -开始播放
     * -按钮位置改为暂停
     * -播放状态改为true
     */
    this.avPlayer?.play()
    this.playerState = '暂停'
    this.isPlaying = true
  }
}

效果

启动app的效果
image.png

播放歌曲的效果
image.png

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐