HarmonyOS实战开发-一次开发,多端部署-音乐专辑

2024-03-31 22:04

本文主要是介绍HarmonyOS实战开发-一次开发,多端部署-音乐专辑,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

基于自适应和响应式布局,实现一次开发、多端部署音乐专辑页面。

相关概念

  • 一次开发,多端部署:一套代码工程,一次开发上架,多端按需部署。支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。
  • 自适应布局:当外部容器大小发生变化时,元素可以根据相对关系自动变化以适应外部容器变化的布局能力。相对关系如占比、固定宽高比、显示优先级等。当前自适应布局能力有7种:拉伸能力、均分能力、占比能力、缩放能力、延伸能力、隐藏能力、折行能力。自适应布局能力可以实现界面显示随外部容器大小连续变化。
  • 响应式布局:当外部容器大小发生变化时,元素可以根据断点、栅格或特定的特征(如屏幕方向、窗口宽高等)自动变化以适应外部容器变化的布局能力。当前响应式布局能力有3种:断点、媒体查询、栅格布局。
  • GridRow:栅格容器组件,仅可以和栅格子组件(GridCol)在栅格布局场景中使用。
  • GridCol:栅格子组件,必须作为栅格容器组件(GridRow)的子组件使用。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release及以上版本。
  • OpenHarmony SDK版本:API version 9及以上版本。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release及以上版本。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  1. 完成DevEco Device Tool的安装
  2. 完成RK3568开发板的烧录

3.搭建开发环境。

  1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
  3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解。

├──common/src/main/ets               // 公共能力层
│  ├──bean
│  │  └──MenuData.ets                // 菜单数据实体类
│  ├──constants
│  │  ├──BreakpointConstants.ets     // 断点常量类
│  │  ├──GridConstants.ets           // 栅格常量类
│  │  └──StyleConstants.ets          // 样式常量类
│  └──utils
│     └──BreakpointSystem.ets        // 断点工具类
├──features                          // 基础特性层
│  ├──content/src/main/ets           // 专辑封面和歌曲列表内容区
│  │  ├──components
│  │  │  ├──AlbumComponent.ets       // 自定义专辑封面组件
│  │  │  ├──AlbumCover.ets           // 支持多设备的自定义专辑封面组件
│  │  │  ├──Content.ets              // 自定义专辑封面和歌曲列表组件
│  │  │  └──PlayList.ets             // 自定义歌曲列表组件
│  │  ├──constants
│  │  │  └──ContentConstants.ets     // 内容区常量类
│  │  └──viewmodel
│  │     ├──SongDataSource.ets       // 懒加载数据源
│  │     └──SongListData.ets         // 歌曲列表数据类
│  ├──content/src/main/resources     // 资源文件目录
│  ├──header/src/main/ets            // 顶部标题栏
│  │  ├──components
│  │  │  └──Header.ets               // 自定义标题栏组件
│  │  └──constants
│  │     └──HeaderConstants.ets      // 标题栏常量类
│  ├──header/src/main/resources      // 资源文件目录
│  └──player/src/main/ets            // 底部播放控制区
│  │  ├──components
│  │  │  └──Player.ets               // 自定义底部播放控制区组件
│  │  └──constants
│  │     └──PlayerConstants.ets      // 播放控制区常量类
│  └──player/src/main/resources      // 资源文件目录
└──products                          // 产品定制层├──phone/src/main/ets             // 支持手机、平板│  ├──entryability│  │  └──EntryAbility.ts          // 程序入口类│  └──pages│     └──MainPage.ets             // 主界面└──phone/src/main/resources       // 资源文件目录

工程结构管理

在这个章节中,我们需要完成模块划分、梳理模块间依赖关系并设计代码结构,从而便于后续复杂项目的维护。参考上一章节页面设计,我们可以将页面分拆为多个组成部分:

  1. 标题栏
  2. 专辑封面
  3. 歌曲列表
  4. 播放控制区

工程结构建议如下:

  • 定义common层(公共能力层): 用于存放项目的工具类、公共常量等。需编译成一个HAR包,只可以被products和features依赖,不可以反向依赖。
  • 定义features层(基础特性层): 用于存放相对独立的各个功能单元、自定义UI组件或业务实现逻辑,供产品灵活部署。不需要单独部署的feature通常编译为HAR包,供products或其它features使用。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行组合部署。features层可以横向调用及依赖common层,同时可以被products层不同设备形态的HAP所依赖,但是不能反向依赖products层。
  • 定义products层(产品定制层): 用于针对不同设备形态进行功能和特性集成。products层各个子目录分别编译为一个Entry类型的HAP包,作为应用的主入口,products层不可以横向调用。

本篇Codelab通过common层管理媒体查询工具类、栅格常量、公共常量等;按照页面分组,将标题栏定义为header基础特性,将专辑封面和歌曲列表定义为content基础特性,将播放控制区定义为player基础特性;定义phone产品目录支持手机、平板等设备,集成基础特性层的能力。products依赖features和common,features依赖common但不互相依赖,工程结构清晰且便于维护。

├──common               // 公共能力层
├──features             // 基础特性层
│  ├──content           // 专辑封面和歌曲列表内容区
│  ├──header            // 顶部标题栏
│  └──player            // 底部播放控制区
└──products             // 产品定制层└──phone             // 支持手机、平板

一次开发,多端部署实现方案

多标题栏

在这个章节中,我们需完成顶部标题栏的实现方案。不同设备的标题栏始终只显示“返回按钮”、“歌单”以及“更多按钮”,中间空白区域通过Blank组件填充,可实现自适应拉伸能力。效果如图所示:

// Header.ets
@Component
export struct Header {@StorageProp('fontSize') fontSize: number = 0;build() {Row() {Image($r('app.media.ic_back')).width($r('app.float.icon_width')).height($r('app.float.icon_height')).margin({ left: $r('app.float.icon_margin') })Text($r('app.string.play_list')).fontSize(this.fontSize + HeaderConstants.TITLE_FONT_SIZE_PLUS).fontWeight(HeaderConstants.TITLE_FONT_WEIGHT).fontColor($r('app.color.title_color')).opacity($r('app.float.title_opacity')).letterSpacing(HeaderConstants.LETTER_SPACING).padding({ left: $r('app.float.title_padding_left') })Blank()Image($r('app.media.ic_more')).width($r('app.float.icon_width')).height($r('app.float.icon_height')).margin({ right: $r('app.float.icon_margin') }).bindMenu(this.getMenu())}.width(StyleConstants.FULL_WIDTH).height($r('app.float.title_bar_height')).zIndex(HeaderConstants.Z_INDEX)}
}

专辑封面

在这个章节中,我们需完成专辑封面的实现方案。专辑封面由图片、歌单介绍及常用操作(“收藏”“下载”“评论”“分享”)三部分组成,这三部分的布局可以用栅格实现。

  • 在sm断点下,图片占4个栅格,歌单介绍占8个栅格,常用操作占12个栅格。
  • 在md断点下,图片、歌单介绍和常用操作分别占12个栅格。
  • 在lg断点下,图片、歌单介绍和常用操作分别占12个栅格。

// AlbumComponent.ets
@Component
export struct AlbumComponent {@StorageProp('coverMargin') coverMargin: number = 0;@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm';...build() {Column() {GridRow() {GridCol({span: { sm: GridConstants.SPAN_FOUR, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE }}) {this.CoverImage()}GridCol({span: { sm: GridConstants.SPAN_EIGHT, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE }}) {this.CoverIntroduction()}GridCol({span: { sm: GridConstants.SPAN_TWELVE, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE }}) {this.CoverOptions()}.padding({top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.option_margin') : 0,bottom: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.option_margin') : 0})}.padding({top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ?$r('app.float.cover_padding_top_sm') : $r('app.float.cover_padding_top_other')})}.margin({left: this.coverMargin,right: this.coverMargin})}
}

对于“收藏”、“下载”、“评论”、“分享”常用操作,通过设置容器组件的justifyContent属性为FlexAlign.SpaceEvenly,实现自适应均分能力。对于专辑图片,设置aspectRatio属性为1,Image组件宽高比固定为1:1,实现自适应缩放能力。

// AlbumComponent.ets
@Component
export struct AlbumComponent {...@BuilderCoverImage() {Stack({ alignContent: Alignment.BottomStart }) {...}.width(StyleConstants.FULL_WIDTH).height(StyleConstants.FULL_HEIGHT).aspectRatio(ContentConstants.ASPECT_RATIO_ALBUM_COVER)}...@BuilderCoverOptions() {Row() {ForEach(optionList, item => {Column({ space: ContentConstants.COVER_OPTION_SPACE }) {Image(item.image).height($r('app.float.option_image_size')).width($r('app.float.option_image_size'))Text(item.text).fontColor($r('app.color.album_name_introduction')).fontSize(this.fontSize - 1)}})}.height($r('app.float.option_area_height')).width(StyleConstants.FULL_WIDTH).justifyContent(FlexAlign.SpaceEvenly)}
}

歌曲列表

在这个章节中,我们需完成歌曲列表的实现方案。对于不同屏幕尺寸的设备,歌单列表的样式基本一致,但sm和md断点下的歌曲列表是单列显示,lg断点下的歌曲列表是双列显示。可以通过List组件的lanes属性实现这一效果。效果如图所示:

// PlayList.ets
@Component
export struct PlayList {@Consume coverHeight: number;@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm';@StorageProp('fontSize') fontSize: number = 0;...build() {Column() {this.PlayAll()List() {LazyForEach(new SongDataSource(songList), (item: SongItem) => {ListItem() {Column() {this.SongItem(item.title, item.label, item.singer)Divider().strokeWidth(ContentConstants.DIVIDER_STROKE_WIDTH).color($r('app.color.black')).opacity($r('app.float.divider_opacity'))}.padding({left: $r('app.float.list_item_padding'),right: $r('app.float.list_item_padding')})}}, (item, index) => JSON.stringify(item) + index)}.width(StyleConstants.FULL_WIDTH).height(StyleConstants.FULL_HEIGHT).backgroundColor($r('app.color.white')).margin({ top: $r('app.float.list_area_margin_top') }).lanes(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ?ContentConstants.COL_TWO : ContentConstants.COL_ONE)}.padding({top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? 0 : $r('app.float.list_area_padding_top'),bottom: $r('app.float.list_area_padding_bottom')})}...}
}

播放控制区

在这个章节中,我们需完成底部播放控制区的实现方案。对于不同屏幕尺寸的设备,播放控制区显示的内容有差异,sm断点仅显示播放按钮,md断点显示上一首、播放和下一首按钮,lg断点显示收藏、上一首、播放、下一首、列表按钮,可以分别设置按钮的displayPriority属性,通过自适应隐藏能力实现。同时,歌曲信息与播放控制按钮之间通过Blank组件自动填充,实现自适应拉伸能力。效果如图所示:

// Player.ets
@Component
export struct Player {...build() {Row() {...Blank()Row() {Image($r('app.media.ic_favorite'))....displayPriority(PlayerConstants.DISPLAY_PRIORITY_ONE)Image($r('app.media.ic_previous'))....displayPriority(PlayerConstants.DISPLAY_PRIORITY_TWO)Image($r('app.media.ic_pause'))....displayPriority(PlayerConstants.DISPLAY_PRIORITY_THREE)Image($r('app.media.ic_next'))....displayPriority(PlayerConstants.DISPLAY_PRIORITY_TWO)Image($r('app.media.ic_music_list'))....displayPriority(PlayerConstants.DISPLAY_PRIORITY_ONE)}.layoutWeight(PlayerConstants.LAYOUT_WEIGHT_PLAYER_CONTROL).justifyContent(FlexAlign.End)}...}
}

特性集成

在这个章节中,我们将标题栏(header基础特性)、专辑内容(content基础特性)、播放控制区(player基础特性)集成到一起,作为应用的主入口。同时,后续项目如果新增更多的产品例如穿戴设备、智慧屏等,可定义新的products产品、自由集成不同的features基础特性,从而达成代码尽量复用、产品自由定制的目标。

// MainPage.ets
@Component
struct MainPage {...build() {Stack({ alignContent: Alignment.Top }) {Header()Content()Player()}...
}

解决系统能力差异的兼容性问题

前面章节主要介绍了如何解决页面适配、工程管理的问题,本章节主要介绍应用如何解决设备系统能力差异的兼容问题。

我们以一个简单例子说明:一个具有音频设备管理功能的应用,如何兼容地运行在有音频设备管理和无音频设备管理系统能力的设备上。

从开发到用户安装、运行,一般要经历几个阶段:应用分发和下载->应用安装->应用运行,要解决上面提到的兼容问题,一般有如下几种解决思路:

  1. 分发和下载:有音频设备管理设备的用户才能在应用市场可见该应用,供用户下载。
  2. 安装:有音频设备管理能力的设备才允许安装该应用。
  3. 运行:在运行阶段通过动态判断方式,在有音频设备管理设备上功能运行正常,在无音频设备管理设备上运行不发生Crash。

所以,对应的解决方案就有:

  1. 在分发阶段,上架的应用使用的系统能力是设备系统能力的子集。满足这个条件,用户才能在应用市场看到该应用。
  2. 在安装阶段,安装的应用调用的系统能力是设备系统能力的子集。满足这个条件,用户才能安装该应用。
  3. 通过canIUse接口判断是否支持某个系统能力。

本篇Codelab以音频设备管理为例,仅介绍canIUse接口判断的方式。点击右上角更多,如果当前系统支持音频设备管理能力,则显示“音频设备管理”菜单选项,否则不显示。

// Header.ets
@Component
export struct Header {...getMenu(): MenuData[] {let menuItem: MenuData = new MenuData();let menuDatas: MenuData[] = [];if (canIUse(HeaderConstants.SYSCAP_ETHERNET)) {menuItem.value = HeaderConstants.AUDIO_DEVICE_SERVICE;menuItem.action = () => {promptAction.showToast({message: HeaderConstants.AUDIO_DEVICE_SERVICE,duration: HeaderConstants.TOAST_DURATION});};menuDatas.push(menuItem);}return menuDatas;}
}

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 如何针对不同屏幕尺寸的设备完成一次开发,多端部署的方案设计。
  2. 如何按照合理的工程结构划分模块、组织代码。
  3. 通过自适应布局和响应式布局方案实现一次开发,多端部署。
  4. 通过运行时判断解决系统能力差异的兼容性问题。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》

这篇关于HarmonyOS实战开发-一次开发,多端部署-音乐专辑的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865465

相关文章

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

nginx部署https网站的实现步骤(亲测)

《nginx部署https网站的实现步骤(亲测)》本文详细介绍了使用Nginx在保持与http服务兼容的情况下部署HTTPS,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录步骤 1:安装 Nginx步骤 2:获取 SSL 证书步骤 3:手动配置 Nginx步骤 4:测

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper