熊熊出没 发表于 2024-12-23 16:54:29

鸿蒙NEXT版实战开辟案例:音乐播放器(附带源码)

往期鸿蒙全套实战精彩文章必看内容:



[*] 鸿蒙开辟焦点知识点,看这篇文章就够了
[*] 最新版!鸿蒙HarmonyOS Next应用开辟实战学习路线
[*] 鸿蒙HarmonyOS NEXT开辟技能最全学习路线指南
[*] 鸿蒙应用开辟实战项目,看这一篇文章就够了(部分项目附源码)
前次写过一次鸿蒙的音乐项目,不过是基于api9,最近幽蓝君把它完善优化,并适配到api 12,也就是最新的鸿蒙NEXT版本,供各人参考。
先来看这次音乐播放器项目的效果图:

https://img-blog.csdnimg.cn/img_convert/8c0d60cb563e6668b66cfb29fa01936e.gif
下面为各人详解一下这个项目。
探索发现

https://img-blog.csdnimg.cn/img_convert/46ebffc7d2408995a4ef9d1ee4b2b674.png
这是个简单的音乐列表页,从全局看,有导航栏说明要用Navigation组件,当然也可以自界说,导航栏部分还有个切换开关,可以使用menu实现:相干代码如下:
@Builder NavigationMenu(){    Row({space:10}){      Text('探索')      .fontColor(Color.Black)      .fontSize(15)      Row()      .width(1)      .height(18)      .backgroundColor(Color.Gray)      Text('推荐')      .fontColor(Color.Gray)      .fontSize(15)    }    .width('100%')    .justifyContent(FlexAlign.Center)    .height(50)    .alignItems(VerticalAlign.Center)}   Navigation(){    //内容部分}  .titleMode(NavigationTitleMode.Mini)  .hideBackButton(true)  .width('100%')  .height('100%')  .backgroundColor('rgba(245, 245, 245, 1)')  .menus(this.NavigationMenu()) 然后,内容部分显然使用List组件实现,可以看到它可以分为3部分,分别是搜索框、今日热歌和歌单遨游指南。前两个部分都比较简单,歌单遨游指南是可以左右滑动的,所以我使用网格组件Grid实现,设置rowsTemplate属性可以使Grid水平方向滑动,相干代码如下:
ListItemGroup({header:this.SectionHeader('歌单遨游指南',false)}){
          ListItem(){
            Grid(){
            ForEach(this.albumList,(item:Album,index)=>{
                GridItem(){
                  Column(){
                  Image(item.cover)
                      .width(150)
                      .height(150)
                  Text(item.title)
                      .fontColor(Color.Black)
                      .fontSize(15)
                      .fontWeight(FontWeight.Bold)
                      .margin({top:5})
                      .maxLines(2)
                  }
                  .alignItems(HorizontalAlign.Center)
                  .width(150)
                  .height(200)
                }
            })
            }
            .width('100%')
            .rowsTemplate('1fr 1fr')
            .rowsGap(12)
            .columnsGap(10)
            .padding({left:12})
            .margin({top:5})
            .scrollBarWidth(0)

          }
          .height(400)
      } 播放音乐页面

https://img-blog.csdnimg.cn/img_convert/d7073f6c4daf2eeb4650b88fb06396bb.png
这个页面看起来很复杂,其实并不难。这里幽蓝君想说的是,布局无非三种方式,横向、竖向和层叠,其他的布局方式都由这三种衍生而来。
所以对于这个界面,只要把它拆分开来就会发现其实很容易:

https://img-blog.csdnimg.cn/img_convert/7a5c2840f4aaf1d139586e4543b89348.png
有一点难度的地方就是中间的图片是旋转的,我们给他设置一个动态的角度,使用定时器控制图片旋转,代码如下:
Image($rawfile(this.musicList.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.value*360 })
            


this.timer = setInterval(() => {
      // 保留2位小数
      this.value = this.value + 0.005

    }, 30); 还有一个地方要说一下,点击列表按钮会弹出一个音乐列表,这里使用bindsheet实现,难倒不难,就是有很多友友不知道这个东西:

https://img-blog.csdnimg.cn/img_convert/92a9f0bc8bb910d2256152aa4d2c98eb.png
相干代码如下:
Column(){

}
.bindSheet($$this.showList, this.listPage(), {
    height: 40 + (58 + 5)*this.musicList.length,
    backgroundColor: Color.White,
    showClose:false,
})


@Builder listPage(){
      Column(){
      Divider()
          .width('100%')
          .height(0.8)
          .backgroundColor('rgb(226,226,226)')
      List({space:0}){
          ForEach(this.musicList,(item:Music,index)=>{
            ListItem(){
            Row(){
                Column(){
                  Text(item.name)
                  .fontColor('#222628')
                  .fontSize(16)
                }
                .alignItems(HorizontalAlign.Start)

                if(this.currentIndex == index && this.isPlay){
                  Image($r('app.media.list_pause'))
                  .width(28)
                  .height(28)
                  .onClick(()=>{
                      this.playOrPause()
                  })
                }else{
                  Image($r('app.media.list_play'))
                  .width(28)
                  .height(28)
                  .onClick(()=>{
                      this.playSong(index)
                  })
                }
            }
            .alignItems(VerticalAlign.Center)
            .justifyContent(FlexAlign.SpaceBetween)
            .width('100%')
            .height(62)
            }
          })
      }
      .divider({strokeWidth:0.5,color:'rgb(236,236,236)'})
      .padding({left:20,right:20})
      }
      .expandSafeArea(,)
      .alignItems(HorizontalAlign.Start)
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius({topLeft:20,topRight:20})
      .shadow({ radius: 30, color: 'rgba(230,230,230,0.7)', offsetX: 0, offsetY: -20 })
} 播放音乐
这个项目中使用本地音乐文件,所以跟各人分享下播放本地的音频文件。起首把文件放到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.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 https://i-blog.csdnimg.cn/direct/5be73c96555f4ad39cb84daabe28288e.png​

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙NEXT版实战开辟案例:音乐播放器(附带源码)