鸿蒙开发实战案例--音乐播放器(付源码)

永远记住,最好的学习方法就是实战,今天和大家分享的是鸿蒙开发的实战项目:音乐播放器。

话不多说,先看项目演示效果:

这个项目开始是在api11版本下开发的,但是为了更多的友友学习,我还是把它向下适配到了api9,所以升级到api11或者api12的友友也可以瞅瞅,改动不大。

下面为大家讲解项目中的一些要点:

布局

页面布局比较简单,一眼望去纵向布局,中间穿插着一些横向布局,从上往下排就可以了。

要注意的是,页面底部还有个音乐列表弹窗,弹窗与主页面是层叠布局,所以主页面和弹窗要用Stack包裹。

因为弹窗是默认隐藏的,所以设置列表弹窗的初始y值为屏幕高度,布局基本代码如下:


@State screenWidth:number = px2vp(display.getDefaultDisplaySync().width)
@State screenHeight:number = px2vp(display.getDefaultDisplaySync().height)
@State listViewPosition:number = this.screenHeight

Stack({alignContent:Alignment.Bottom}){
      //主页面
      Flex({direction:FlexDirection.Column,justifyContent:FlexAlign.SpaceBetween}){
        
      }
      .width('100%')
      .height('100%')
      .padding({left:20,right:20})
      //背景色渐变
      .radialGradient({
        center: [this.screenWidth/2, this.screenHeight/2],
        radius: this.screenWidth*2,
        colors: [[0xE0EEFF, 0.0], [0xE4F2FF, 0.3], [0xFBD4FF, 1]]
      })

      //底部弹窗
      ListView({musicList:$musicList)
          //设置初始位置
        .position({y:this.listViewPosition})

    }

关于弹窗的弹出和隐藏动画,会在下面动画部分讲解。

动画

本项目中的动画主要有两个,首先是音乐封面的匀速旋转,我选择使用计时器setInterval()来改变图片的角度:

  //初始角度为0
  @State angle: number = 0;

  //点击播放按钮执行动画
  startRotate() {
    this.timer = setInterval(() => {
      //保留2位小数
      this.angle = this.angle + 0.005
    }, 30);
  }

  //音乐封面
  Image($rawfile(this.musicList[this.currentIndex].cover))
            .width(this.screenWidth - 50)
            .height(this.screenWidth - 50)
            .borderRadius((this.screenWidth - 50)/2)
            .objectFit(ImageFit.Fill)
            .rotate({ x: 0, y: 0, z: 1, angle: this.angle*360 })

接下来是弹窗的出现和隐藏,前面说了弹窗一开始在屏幕底部看不到的位置,点击列表按钮时使用animateTo给它设置一个正确的y值即可实现由下向上的动画效果:

animateTo({
            //动画时间
           duration: 200,
       }, () => {
             //计算弹窗高度
            let y = 20 + 40 + 62*this.musicList.length
            //设置y值
            this.listViewPosition = this.screenHeight - y
       })

隐藏弹窗的动画相对麻烦一些,我做了一个类似手机屏幕底部的横条,在下拉横条的时候隐藏动画。

所以要给图中横条添加下滑手势,在下滑手势结束时使用刚才的方法将y值恢复初始值:

private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Down })

Row(){
   }
   .width(40)
   .height(4)
   .backgroundColor('rgb(234,235,237)')
   .borderRadius(2)
   .gesture(
        PanGesture(this.panOption)
          .onActionStart((event?: GestureEvent) => {
            //识别到下拉手势
            console.info('Pan start')
          })
          .onActionUpdate((event?: GestureEvent) => {
            if (event) {
             //下拉手势执行中
              console.info('event:',JSON.stringify(event))
            }
          })
          .onActionEnd(() => {
            console.info('Pan end')
             //下拉手势结束
             animateTo({
                duration: 200,
              }, () => {
                this.listViewPosition = this.screenHeight
              })
          })
      )

播放音频

本文使用AVPlayer来播放音频,音频素材是本地rawfile中的文件,您也可以选择使用网络音频,注意播放网络音频要申请网络权限,它们的创建方法分别如下:

播放rawfile文件:

// 创建avPlayer实例对象
let avPlayer: media.AVPlayer = await media.createAVPlayer();
// 创建状态机变化回调函数
this.setAVPlayerCallback(avPlayer);
// 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
// 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度
let context = getContext(this) as common.UIAbilityContext;
let fileDescriptor = await context.resourceManager.getRawFd(this.musicList[this.currentIndex].url);
let avFileDescriptor: media.AVFileDescriptor =
     { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
this.isSeek = false; // 支持seek操作
// 为fdSrc赋值触发initialized状态机上报
avPlayer.fdSrc = avFileDescriptor;
this.avPlayer = avPlayer

 播放网络音频:

// 创建avPlayer实例对象
let avPlayer: media.AVPlayer = await media.createAVPlayer();
// 创建状态机变化回调函数
this.setAVPlayerCallback(avPlayer);
this.isSeek = false; // 不支持seek操作
avPlayer.url = 'https://cdn.soundstripe.com/uploads/audio_file/381e129db0964d5289ebdaec01f61bf1/mp3_PALA_OneMoreDance_Full.mp3?token=1712968809_026183eab08f6d0f3a7aa5d422212a980252cc82b135e6a9950ee00fc8d74b73';
this.avPlayer = avPlayer

第二步,为avPlayer设置回调函数:

// 注册avplayer回调函数
  setAVPlayerCallback(avPlayer: media.AVPlayer) {

    avPlayer.on('timeUpdate', (seekDoneTime: number) => {

      if(this.duration > 0){
        let progress = seekDoneTime/this.duration
        this.progressNow = progress*100
      }
      this.progressTimeString = this.durationToTimeString(seekDoneTime)
    })
    // seek操作结果回调函数
    avPlayer.on('seekDone', (seekDoneTime: number) => {
      console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
    })

    // 状态机变化回调函数
    avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
      switch (state) {
        case 'idle': // 成功调用reset接口后触发该状态机上报
          console.info('AVPlayer state idle called.');
          avPlayer.release(); // 调用release接口销毁实例对象
          break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报
          console.info('AVPlayer state initialized called.');
          avPlayer.prepare();
          break;
        case 'prepared': // prepare调用成功后上报该状态机
          console.info('AVPlayer state prepared called.');
          if(this.isPlay){
            avPlayer.play();
          }
          this.duration = avPlayer.duration
          this.durationTimeString = this.durationToTimeString(this.duration)
          console.log('duration:',avPlayer.duration)

          break;
        case 'playing': // play成功调用后触发该状态机上报
          console.info('AVPlayer state playing called.');
          if (this.count !== 0) {
            if (this.isSeek) {
              console.info('AVPlayer start to seek.');
              avPlayer.seek(avPlayer.duration); //seek到音频末尾
            } else {
              // 当播放模式不支持seek操作时继续播放到结尾
              console.info('AVPlayer wait to play end.');
            }
          } else {
            // avPlayer.pause(); // 调用暂停接口暂停播放
          }
          this.count++;
          break;
        case 'paused': // pause成功调用后触发该状态机上报
          console.info('AVPlayer state paused called.');
        // avPlayer.play(); // 再次播放接口开始播放
          break;
        case 'completed': // 播放结束后触发该状态机上报
          console.info('AVPlayer state completed called.');
        // avPlayer.stop(); //调用播放结束接口
        // this.endRotate();
          if(this.currentIndex < this.musicList.length - 1){
            this.currentIndex += 1
            this.changeSong()
          }else {
            this.endRotate();
          }
          break;
        case 'stopped': // stop接口成功调用后触发该状态机上报
          console.info('AVPlayer state stopped called.');
        // avPlayer.reset(); // 调用reset接口初始化avplayer状态
          break;
        case 'released':
          console.info('AVPlayer state released called.');
          break;
        default:
          console.info('AVPlayer state unknown called.');
          break;
      }
    })
  }

 下面是AVPlayer的常用方法:


//播放
this.avPlayer.play() 
//暂停
this.avPlayer.pause() 
//结束
this.avPlayer.stop() 
//重置
this.avPlayer.reset() 
//销毁释放
this.avPlayer.release()

对于如何进行切换歌曲,只需要重置当前AVPlayer对象并重新初始化即可。 

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料

 获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

 有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

  • 23
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
鸿蒙开发app实战源码是指使用鸿蒙系统开发应用程序所涉及的源代码示例。鸿蒙系统是由华为公司推出的全场景分布式操作系统,旨在为开发者提供更简单、高效的应用开发环境。 鸿蒙开发app实战源码包括了开发鸿蒙应用所需的各种源代码示例,其中包括界面布局、事件处理、数据交互等核心功能的实现方法。通过学习这些源码示例,开发人员可以更深入了解鸿蒙系统开发方式和特性,从而更快地上手开发鸿蒙应用。 鸿蒙开发app实战源码通常包含以下几个方面的内容: 1. 页面布局示例:展示了如何使用鸿蒙系统提供的布局组件来构建应用界面,包括线性布局、网格布局等。 2. 事件处理示例:介绍了如何处理用户的各种操作事件,比如点击、触摸等,以及如何响应这些事件并进行相应的处理逻辑。 3. 数据交互示例:演示了如何使用鸿蒙系统提供的数据交互接口与后台服务器进行数据交互,包括发送请求、接收响应等。 通过实际的源码示例,开发人员可以更加直观地了解鸿蒙系统开发流程和技术细节,加速自己的学习和开发进度。同时,源码示例也为开发人员提供了一些最佳实践和经验总结,可以帮助他们更好地编写高质量的鸿蒙应用代码。 总之,鸿蒙开发app实战源码开发者学习和掌握鸿蒙应用开发的重要资源,通过学习这些源码示例,开发人员能够更好地开发出符合用户需求和有良好用户体验的鸿蒙应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值