[文章]OpenHarmony标准设备应用开发(二)——布局、动画与音乐

阅读量0
0
3
(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)

邢碌

上一章我们讲解了应用编译环境准备,设备编译环境准备,开发板烧录,将一个最简单的 OpenAtom OpenHarmony(以下简称“OpenHarmony”)程序安装到我们的标准设备上。

本章是 OpenHarmony 标准设备应用开发的第二篇文章。我们通过知识体系新开发的几个基于 OpenHarmony3.1 Beta 标准系统的样例:分布式音乐播放、传***、购物车等样例,分别介绍下音乐播放、显示动画、动画转场(页面间转场)三个进阶技能。首先我们来讲如何在 OpenHarmony 中实现音乐的播放。

一、分布式音乐播放

通过分布式音乐播放器,大家可以学到一些 ArkUI 组件和布局在 OpenHarmony 中是如何使用的,以及如何在自己的应用中实现音乐的播放,暂停等相关功能。应用效果如下图所示:


1.1 界面布局

整体布局效果如下图所示:



首先是页面整体布局,部分控件是以模块的方式放在整体布局中的,如 operationPannel()、sliderPannel()、playPannel() 模块。页面整体布是由 Flex 控件中,包含 Image、Text 以及刚才所说的三个模块所构成。

  1. build() {
  2.     Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
  3.       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
  4.         Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End }) {
  5.           Image($r("app.media.icon_liuzhuan")).width(32).height(32)
  6.         }.padding({ right: 32 }).onClick(() => {
  7.           this.onDistributeDevice()
  8.         })

  9.         Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center }) {
  10.           Image($r("app.media.Bg_classic")).width(312).height(312)
  11.         }.margin({ top: 24 })

  12.         Text(this.currentMusic.name).fontSize(20).fontColor("#e6000000").margin({ top: 10 })
  13.         Text("未知音乐家").fontSize(14).fontColor("#99000000").margin({ top: 10 })
  14.       }.flexGrow(1)


  15.       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.End }) {
  16.         this.operationPannel()
  17.         this.sliderPannel()
  18.         this.playPannel()
  19.       }.height(200)
  20.     }
  21.     .linearGradient({
  22.       angle: 0,
  23.       direction: GradientDirection.Bottom,
  24.       colors: this.currentMusic.backgourdColor
  25.     }).padding({ top: 48, bottom: 24, left: 24, right: 24 })
  26.     .width('100%')
  27.     .height('100%')
  28.   }
复制代码

operationPannel() 模块的布局,该部分代码对应图片中的心形图标,下载图标,评论图标更多图标这一部分布局。其主要是在 Flex 中包含 Image 所构成代码如下:

  1. [url=home.php?mod=space&uid=2743618]@builder[/url] operationPannel() {
  2.     Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
  3.       Image($r("app.media.icon_music_like")).width(24).height(24)
  4.       Image($r("app.media.icon_music_download")).width(24).height(24)
  5.       Image($r("app.media.icon_music_comment")).width(24).height(24)
  6.       Image($r("app.media.icon_music_more")).width(24).height(24)
  7.     }.width('100%').height(49).padding({ bottom: 25 })
  8.   }
复制代码

sliderPannel() 模块代码布局。该部分对应图片中的显示播放时间那一栏的控件。整体构成是在 Flex 中,包含 Text、Slider、Text 三个控件。具体代码如下:

  1. @Builder sliderPannel() {
  2.     Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
  3.       Text(this.currentTimeText).fontSize(12).fontColor("ff000000").width(40)
  4.       Slider({
  5.         value: this.currentProgress,
  6.         min: 0,
  7.         max: 100,
  8.         step: 1,
  9.         style: SliderStyle.INSET
  10.       })
  11.         .blockColor(Color.White)
  12.         .trackColor(Color.Gray)
  13.         .selectedColor(Color.Blue)
  14.         .showSteps(true)
  15.         .flexGrow(1)
  16.         .margin({ left: 5, right: 5 })
  17.         .onChange((value: number, mode: SliderChangeMode) => {
  18.           if (mode == 2) {
  19.             CommonLog.info('aaaaaaaaaaaaaa1: ' + this.currentProgress)
  20.             this.onChangeMusicProgress(value, mode)
  21.           }
  22.         })

  23.       Text(this.totalTimeText).fontSize(12).fontColor("ff000000").width(40)

  24.     }.width('100%').height(18)
  25.   }
复制代码

playPannel() 模块代码对应图片中的最底部播放那一栏五个图标所包含的一栏。整体布局是 Flex 方向为横向,其中包含五个 Image 所构成。具体代码如下:

  1. @Builder playPannel() {
  2.     Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
  3.       Image($r("app.media.icon_music_changemode")).width(24).height(24).onClick(() => {
  4.         this.onChangePlayMode()
  5.       })
  6.       Image($r("app.media.icon_music_left")).width(32).height(32).onClick(() => {
  7.         this.onPreviousMusic()
  8.       })
  9.       Image(this.isPlaying ? $r("app.media.icon_music_play") : $r("app.media.icon_music_stop"))
  10.         .width(80)
  11.         .height(82)
  12.         .onClick(() => {
  13.           this.onPlayOrPauseMusic()
  14.         })
  15.       Image($r("app.media.icon_music_right")).width(32).height(32).onClick(() => {
  16.         this.onNextMusic()
  17.       })
  18.       Image($r("app.media.icon_music_list")).width(24).height(24).onClick(() => {
  19.         this.onShowMusicList()
  20.       })
  21.     }.width('100%').height(82)
  22.   }
复制代码

希望通过上面这些布局的演示,可以让大家学到一些部分控件在 OpenHarmony 中的运用,这里使用的 Arkui 布局和 HarmonyOS* 是一致的,目前 HarmonyOS* 手机还没有发布 Arkui 的版本,大家可以在 OpenHarmony 上抢先体验。常用的布局和控件还有很多,大家可以在下面的链接中找到更多的详细信息。

*编者注:HarmonyOS 是基于开放原子开源基金会旗下开源项目 OpenHarmony 开发的面向多种全场景智能设备的商用版本。是结合其自有特性和能力开发的新一代智能终端操作系统。


1.2 播放音乐

  1. play(seekTo) {
  2.     if (this.player.state == 'playing' && this.player.src == this.getCurrentMusic().url) {
  3.       return
  4.     }

  5.     if (this.player.state == 'idle' || this.player.src != this.getCurrentMusic().url) {
  6.       CommonLog.info('Preload music url = ' + this.getCurrentMusic().url)
  7.       this.player.reset()
  8.       this.player.src = this.getCurrentMusic().url
  9.       this.player.on('dataLoad', () => {
  10.         CommonLog.info('dataLoad duration=' + this.player.duration)
  11.         this.totalTimeMs = this.player.duration
  12.         if (seekTo > this.player.duration) {
  13.           seekTo = -1
  14.         }
  15.         this.player.on('play', (err, action) => {
  16.           if (err) {
  17.             CommonLog.info(`MusicPlayer[PlayerModel] error returned in play() callback`)
  18.             return
  19.           }
  20.           if (seekTo > 0) {
  21.             this.player.seek(seekTo)
  22.           }
  23.         })

  24.         this.player.play()
  25.         this.statusChangeListener()
  26.         this.setProgressTimer()
  27.         this.isPlaying = true
  28.       })
  29.     }
  30.     else {
  31.       if (seekTo > this.player.duration) {
  32.         seekTo = -1
  33.       }
  34.       this.player.on('play', (err, action) => {
  35.         if (err) {
  36.           CommonLog.info(`MusicPlayer[PlayerModel] error returned in play() callback`)
  37.           return
  38.         }
  39.         if (seekTo > 0) {
  40.           this.player.seek(seekTo)
  41.         }
  42.       })

  43.       this.player.play()
  44.       this.setProgressTimer()
  45.       this.isPlaying = true
  46.     }
  47.   }
复制代码

1.3 音乐暂停

  1. pause() {
  2.     CommonLog.info("pause music")
  3.     this.player.pause();
  4.     this.cancelProgressTimer()
  5.     this.isPlaying = false
  6.   }
复制代码

接下来我们讲解如何在 OpenHarmony 中实现显示动画的效果。

二、显示动画

我们以传***小游戏中的显示动画效果为例,效果如下图所示。
通过本小节,大家在上一小节的基础上,学到更多 ArkUI 组件和布局在 OpenHarmony 中的应用,以及如何在自己的应用中实现显示动画的效果。



实现步骤:

2.1 编写弹窗布局:将游戏失败文本、***图片和再来一局按钮图片放置于 Column 容器中;

2.2 用变量来控制动画起始和结束的位置:用 Flex 容器包裹***图片,并用 @State 装饰变量 toggle,通过变量来动态修改 Flex 的 direction 属性;布局代码如下:

  1. @State toggle: boolean = true
  2. private controller: CustomDialogController
  3. [url=home.php?mod=space&uid=3273389]@consume[/url] deviceList: RemoteDevice[]
  4. private confirm: () => void
  5. private interval = null

  6. build() {
  7.    Column() {
  8.       Text('游戏失败').fontSize(30).margin(20)
  9.       Flex({
  10.          direction: this.toggle ? FlexDirection.Column : FlexDirection.ColumnReverse,
  11.          alignItems: ItemAlign.Center
  12.       })
  13.       {
  14.          Image($r("app.media.bomb")).objectFit(ImageFit.Contain).height(80)
  15.       }.height(200)

  16.       Image($r("app.media.btn_restart")).objectFit(ImageFit.Contain).height(120).margin(10)
  17.          .onClick(() => {
  18.                this.controller.close()
  19.                this.confirm()
  20.          })
  21.    }
  22.    .width('80%')
  23.    .margin(50)
  24.    .backgroundColor(Color.White)
  25. }
复制代码

2.3 设置动画效果:使用 animateTo 显式动画接口***位置切换时添加动画,并且设置定时器定时执行动画,动画代码如下:

  1. aboutToAppear() {
  2.    this.setBombAnimate()
  3. }

  4. setBombAnimate() {
  5.    let fun = () => {
  6.       this.toggle = !this.toggle;
  7.    }
  8.    this.interval = setInterval(() => {
  9.       animateTo({ duration: 1500, curve: Curve.Sharp }, fun)
  10.    }, 1600)
  11. }
复制代码

三、转场动画(页面间转场)

我们同样希望在本小节中,可以让大家看到更多的 ArkUI 中的组件和布局在 OpenHarmony 中的使用,如何模块化的使用布局,让自己的代码更简洁易读,以及在应用中实现页面间的转场动画效果。

下图是分布式购物车项目中的转场动画效果图:


页面布局效果图:


整体布局由 Column、Scroll、Flex、Image 以及 GoodsHome()、MyInfo()、HomeBottom() 构成,该三个模块我们会分别说明。具体代码如下:

  1. build() {
  2.     Column() {
  3.       Scroll() {
  4.         Column() {
  5.           if (this.currentPage == 1) {
  6.             Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End }) {
  7.               Image($r("app.media.icon_share"))
  8.                 .objectFit(ImageFit.Cover)
  9.                 .height('60lpx')
  10.                 .width('60lpx')
  11.             }
  12.             .width("100%")
  13.             .margin({ top: '20lpx', right: '50lpx' })
  14.             .onClick(() => {
  15.               this.playerDialog.open()
  16.             })

  17.             GoodsHome({ goodsItems: this.goodsItems})
  18.           }
  19.           else if (this.currentPage == 3) {
  20.             //我的
  21.             MyInfo()
  22.           }
  23.         }
  24.         .height('85%')
  25.       }
  26.       .flexGrow(1)

  27.       HomeBottom({ remoteData: this.remoteData})

  28.     }
  29.     .backgroundColor("white")
  30.   }
复制代码

GoodsHome() 模块对应效果图中间显示商品的部分,其主要结构为 TabContent 中包含的两个 List 条目所构成。具体代码如下:

  1. @Component
  2. struct GoodsHome {
  3.   private goodsItems: GoodsData[]
  4.   @Consume ShoppingCartsGoods :any[]
  5.   build() {
  6.     Column() {
  7.       Tabs() {
  8.         TabContent() {
  9.           GoodsList({ goodsItems: this.goodsItems});
  10.         }
  11.         .tabBar("畅销榜")
  12.         .backgroundColor(Color.White)

  13.         TabContent() {
  14.           GoodsList({ goodsItems: this.goodsItems});
  15.         }
  16.         .tabBar("推荐")
  17.         .backgroundColor(Color.White)
  18.       }
  19.       .barWidth(500)
  20.       .barHeight(50)
  21.       .scrollable(true)
  22.       .barMode(BarMode.Scrollable)
  23.       .height('980lpx')

  24.     }
  25.     .alignItems(HorizontalAlign.Start)
  26.     .width('100%')
  27.   }
  28. }
复制代码

上面代码中的 GoodsList() 为每个 list 条目对应显示的信息,会便利集合中的数据,然后显示在对用的 item 布局中,具体代码如下:

  1. @Component
  2. struct GoodsList {
  3.   private goodsItems: GoodsData[]
  4.   @Consume ShoppingCartsGoods :any[]
  5.   build() {
  6.     Column() {
  7.       List() {
  8.         ForEach(this.goodsItems, item => {
  9.           ListItem() {
  10.             GoodsListItem({ goodsItem: item})
  11.           }
  12.         }, item => item.id.toString())
  13.       }
  14.       .width('100%')
  15.       .align(Alignment.Top)
  16.       .margin({ top: '10lpx' })
  17.     }
  18.   }
  19. }
复制代码

最后就是 list 的 item 布局代码。具体代码如下:

  1. @Component
  2. struct GoodsListItem {
  3.   private goodsItem: GoodsData
  4.   @State scale: number = 1
  5.   @State opacity: number = 1
  6.   @State active: boolean = false
  7.   @Consume ShoppingCartsGoods :any[]
  8.   build() {
  9.     Column() {
  10.       Navigator({ target: 'pages/DetailPage' }) {
  11.         Row({ space: '40lpx' }) {
  12.           Column() {
  13.             Text(this.goodsItem.title)
  14.               .fontSize('28lpx')
  15.             Text(this.goodsItem.content)
  16.               .fontSize('20lpx')
  17.             Text('¥' + this.goodsItem.price)
  18.               .fontSize('28lpx')
  19.               .fontColor(Color.Red)
  20.           }
  21.           .height('160lpx')
  22.           .width('50%')
  23.           .margin({ left: '20lpx' })
  24.           .alignItems(HorizontalAlign.Start)

  25.           Image(this.goodsItem.imgSrc)
  26.             .objectFit(ImageFit.ScaleDown)
  27.             .height('160lpx')
  28.             .width('40%')
  29.             .renderMode(ImageRenderMode.Original)
  30.             .margin({ right: '20lpx', left: '20lpx' })

  31.         }
  32.         .height('180lpx')
  33.         .alignItems(VerticalAlign.Center)
  34.         .backgroundColor(Color.White)
  35.       }
  36.       .params({ goodsItem: this.goodsItem ,ShoppingCartsGoods:this.ShoppingCartsGoods})
  37.       .margin({ left: '40lpx' })
  38.     }
  39.   }
复制代码

备注:MyInfo() 模块对应的事其它也免得布局,这里就不做说明。

最后我们来说一下,页面间的页面间的转场动画,其主要是通过在全局 pageTransition 方法内配置页面入场组件和页面退场组件来自定义页面转场动效。具体代码如下:

  1. // 转场动画使用系统提供的多种默认效果(平移、缩放、透明度等)
  2.   pageTransition() {
  3.     PageTransitionEnter({ duration: 1000 })
  4.       .slide(SlideEffect.Left)
  5.     PageTransitionExit({ duration: 1000  })
  6.       .slide(SlideEffect.Right)
  7.   }
  8. }
复制代码

通过上述讲解,我们就在自己的代码中实现音乐的播放,显示动画,页面间转场动画等效果。

在接下来的一章中,我们会讲解如何在 OpenHarmony 通过分布式数据管理,实现设备之间数据如何同步刷新。

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友