HarmonyOS实战开发-一次开发,多端部署-视频应用

news2024/11/26 0:54:48

介绍

随着智能设备类型的不断丰富,用户可以在不同的设备上享受同样的服务,但由于设备形态不尽相同,开发者往往需要针对具体设备修改或重构代码,以实现功能完整性和界面美观性的统一。OpenHarmony为开发者提供了“一次开发,多端部署”的系统能力,让开发者可以基于一次开发,快速构建不同类型终端上的应用,降低开发成本,提高开发效率。

本篇Codelab基于“一次开发,多端部署”提供的自适应布局和响应式布局能力,实现了常见的视频播放应用的主界面。通过三层工程结构尽可能复用了部分代码,并根据设备尺寸的区别设计了对应的页面以兼顾美观和易用。应用被打开时会根据具体的设备形态显示对应的UI界面,其中RK3568开发板的首页效果如图所示:

相关概念

  • 一次开发,多端部署:指一套代码工程,一次开发上架,多端按需部署,目标是支撑开发者高效地开发支持多种终端设备形态的应用。
  • 自适应布局:当外部容器大小发生变化时,元素可以根据相对关系自动变化以适应外部容器变化的布局能力。相对关系如占比、固定宽高比、显示优先级等。当前自适应布局能力有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只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

“一次开发,多端部署”推荐使用三层目录的工程结构来管理工程,上层目录包括common、features和product,common为公共特性目录,存放不同形态设备公用的类和常量,features为功能模块目录,存放应用的各个功能模块,product为产品层目录,存放不同形态设备范类代码。本Codelab不涉及功能特性,因此只存在common、product两个分层。

├──common                                // 公共能力层
│  ├──src/main/ets
│  │  ├──constants
│  │  │  └──CommonConstants.ets          // 公共常量类
│  │  ├──utils
│  │  │  └──BreakpointSystem.ets         // 断点工具类
│  │  └──viewmodel                       // 资源类接口
│  │     ├──BottomTabsItem.ets
│  │     ├──DriveTabsItem.ets  
│  │     ├──FindTabsItem.ets
│  │     ├──HomeTabsItem.ets
│  │     └──MineTabsItem.ets
│  └──src/main/resources                 // 资源文件夹
└──product                               // 产品定制层
   ├──default/src/main/ets               // 支持手机(含折叠屏)、平板
   │  ├──entryability
   │  │  └──EntryAbility.ts              // 程序入口类
   │  ├──pages
   │  │  └──MainPage.ets                 // 主页面
   │  ├──view
   │  │  ├──BottomTabsComponent.ets      // 底部页签组件
   │  │  ├──DriveTabsComponent.ets       // 云盘页组件
   │  │  ├──FindTabsComponent.ets        // 发现页组件
   │  │  ├──HomeTabsComponent.ets        // 首页组件
   │  │  ├──LeftTabsComponent.ets        // 侧边栏组件
   │  │  ├──MineTabsComponent.ets        // 个人页组件
   │  │  ├──RecentlyPlayedComponent.ets  // “最近播放”列表
   │  │  └──RecommendComponent.ets       // “为你推荐”列表
   │  └──viewmodel
   │     ├──BottomTabsModel.ets          // 底部页签model
   │     ├──DriveTabsModel.ets           // 云盘页model
   │     ├──FindTabsModel.ets            // 发现页model
   │     ├──HomeTabsModel.ets            // 首页model
   │     └──MineTabsModel.ets            // 个人页model
   └──default/src/main/resources         // 资源文件夹

主页面框架设计

为了操作便捷和充分利用不同形态设备的屏幕空间,按屏幕宽度的大小将设备划分为3类:

  • sm:320vp<=width<520vp,典型设备为手机。
  • md:520vp<=width<840vp,典型设备为折叠屏。
  • lg:840vp<=width,典型设备为平板或PC。

根据用户使用场景,当操作设备尺寸为sm或md时,一般为竖向使用,此时用于切换应用页面的页签栏适合置于底部。当操作设备尺寸为lg时,一般为横向使用,此时页签栏适合置于左侧。

// MainPage.ets
@Entry
@Component
struct MainPage {
  ...
  build() {
    SideBarContainer(SideBarContainerType.Embed) {
      LeftTabs({ bottomTabIndex: $bottomTabIndex });  // 侧边栏

      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.End, justifyContent: FlexAlign.End }) {
        Tabs({ barPosition: BarPosition.End, index: 0, controller: this.controller }) {
          ... // 页面内容
        }
        if (this.currentBreakpoint !== Const.LG) {
          BottomTabs({ bottomTabIndex: $bottomTabIndex })  // 底部栏,当屏幕尺寸不为"lg"时显示
        }
      }
      .width(Const.FULL_SIZE)
      .backgroundColor($r('app.color.background_color'))
    }
    .showSideBar(this.currentBreakpoint === Const.LG)  // 当屏幕尺寸为"lg"时显示侧边栏
    .showControlButton(false)
    .sideBarWidth(Const.SIDEBAR_WIDTH)
    .maxSideBarWidth(Const.SIDEBAR_WIDTH_MAX)
    .minSideBarWidth(Const.SIDEBAR_WIDTH_MIN)
  }
}

各页面代码实现

首页

首页显示轮播图和“最近播放”、“为你推荐”两个列表,轮播图根据屏幕尺寸的区别,有显示数量的不同(sm为1,md为2,lg为3),列表使用具备自适应布局能力的List组件。

// HomeTabsComponent.ets
@Component
export struct HomeTabs {
  @Link currentBreakpoint: string;
  private scroller: Scroller = new Scroller();
  build() {
    Scroll(this.scroller) {
      GridRow({
        // 设置sm、md和lg的布局列数分别为4、8、12
        columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
        gutter: { x: $r('app.float.gutter_home') },
        breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
      }) {
        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          ... // 标题
        }
        .height($r('app.float.title_height'))
        .margin({ bottom: $r('app.float.home_margin1') })

        // 搜索栏在sm、md下占满全部列,在lg下占8列
        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_8 } }) {
          ... // 搜索栏
        }
        .height($r('app.float.home_grid_height1'))

        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          Swiper() {
            ...
          }
          .height($r('app.float.home_swiper_height'))
          .itemSpace(Const.ITEM_SPACE)
          // 根据屏幕尺寸大小选择不同的轮播图数量
          .displayCount(this.currentBreakpoint === Const.LG ?
          Const.NUM_3 : (this.currentBreakpoint === Const.MD ? Const.NUM_2 : Const.NUM_1))
        }
        .height($r('app.float.home_grid_height2'))

        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          ... // ”最近播放”列表
        }
        .height($r('app.float.home_grid_height3'))

        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          ... // ”为你推荐”列表 
        }
        .height($r('app.float.home_column_height'))
      }
      .height(Const.FULL_SIZE)
    }
    ...
  }
}

发现页

发现页使用栅格布局实现“一次开发,多端部署”能力,把sm设置为4列,md设置为8列,lg设置为12列。热播榜单在不同设备尺寸上分别占据4列、6列和8列。

// FindTabsComponent.ets
@Component
export struct FindTabs {
  private scroller: Scroller = new Scroller();
  build() {
    Scroll(this.scroller) {
      GridRow({
        // 设置sm、md和lg的布局列数分别为4、8、12
        columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
        gutter: { x: $r('app.float.gutter_find') },
        breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
      }) {
        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          ... // 标题
        }
        .height($r('app.float.title_height'))

        LazyForEach(new FindDataSource(FindTabsList), (item: FindTabsItem) => {
          // 设置热播榜单在sm、md和lg上分别占据4、6、8列,并且设置offset属性保证在不同设备形态上都能保持居中
          GridCol({
            span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_6, lg: Const.GRID_8 },
            offset: {
              md: FindTabsList.indexOf(item) === Const.OFFSET_0 ? Const.OFFSET_1 : Const.OFFSET_2,
              lg: FindTabsList.indexOf(item) === Const.OFFSET_0 ? Const.OFFSET_2 : Const.OFFSET_4
            }
          }) {
            ... // 榜单内容
          }
        }, (item: FindTabsItem) => JSON.stringify(item))
      }
    }
    ...
  }
}

RK3568开发板上发现页的实际效果如图所示:

云盘页

云盘页的栅格划分和发现页相同,但是每个子组件在所有屏幕尺寸上都只占据2列。

// DriveTabsComponent.ets
@Component
export struct DriveTabs {
  private scroller: Scroller = new Scroller();
  build() {
    Scroll(this.scroller) {
      GridRow({
        // 设置sm、md和lg的布局列数分别为4、8、12
        columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
        gutter: { x : $r('app.float.gutter_drive') },
        breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
      }) {
        GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
          ... // 标题
        }
        .height($r('app.float.title_height'))

        ForEach(DriveList, (item: DriveTabsItem) => {
          // 设置云盘内容在sm、md和lg上均占据2列
          GridCol({ span: { xs: Const.NUM_2, sm: Const.NUM_2, md: Const.NUM_2, lg: Const.NUM_2 } }) {
            ... // 云盘内容
          }
        }, (item: DriveTabsItem) => JSON.stringify(item))
      }
    }
    ...
  }
}

RK3568开发板上云盘页的实际效果如图所示:

个人页

个人页的栅格划分仍然和发现页相同,但子组件在sm、md形态下占满全部列,在lg形态下只占据8列。

// MineTabsComponent.ets
@Component
export struct MineTabs {
  private scroller: Scroller = new Scroller();
  build() {
    Scroll(this.scroller) {
      GridRow({
        // 设置sm、md和lg的布局列数分别为4、8、12
        columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
        gutter: { x: $r('app.float.gutter_mine') },
        breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
      }) {
        // 设置个人页在sm和md上占满全部列,在lg上占8列,为保证居中在lg上设置offset为2列
        GridCol({
          span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_8 },
          offset: { lg: Const.OFFSET_2 }
        }) {
          ... // 个人页内容
        }
      }
      .height(Const.FULL_SIZE)
      .backgroundColor($r('app.color.mine_background_color'))
    }
    ...
  }
}

RK3568开发板上个人页的实际效果如图所示:

总结

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

  1. 根据设备尺寸形态设计不同的页面布局。
  2. 使用栅格布局实现“一次开发,多端部署”能力。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的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)开发学习手册》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1558991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue watch 深度监听

vue2文档&#xff1a;API — Vue.js vue3文档&#xff1a;侦听器 | Vue.js watch 可以用来监听页面中的数据&#xff0c;但如果监听的源是对象或数组&#xff0c;则使用深度监听&#xff0c;强制深度遍历源&#xff0c;以便在深度变更时触发回调。 一&#xff0c;监听 <t…

RecyclerView 调用 notifyItemInserted 自动滚动到底部的问题

项目中发现一个奇怪的现象 RecyclerView 加载完数据以后&#xff0c;调用 notifyItemInserted 方法&#xff0c;RecyclerView 会滑动到底部。 简化后的效果图&#xff1a; 因为这个 RecyclerView 的适配器有一个 FootViewHolder&#xff0c;所以怀疑是 FootViewHolder 的问题…

选择排序---算法

1、算法概念 首先在未排序列中找到最小(大)元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小(大)元素&#xff0c;然后放到已排序序列的末尾。以此类推&#xff0c;直到所有元素均排序完毕。 选择排序的思想其实和冒泡排…

LLaMA-Factory参数的解答

打开LLaMA-Factory的web页面会有一堆参数 &#xff0c;但不知道怎么选&#xff0c;选哪个&#xff0c;这个文章详细解读一下&#xff0c;每个参数到底是什么含义这是个人写的参数解读&#xff0c;我并非该领域的人如果那个大佬看到有参数不对请反馈一下&#xff0c;或者有补充的…

【网络安全】常见的网站攻击方式及危害

常见的网站攻击方式多种多样&#xff0c;每一种都有其独特的特点和危害。以下是一些常见的网站攻击方式&#xff1a; 跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a;攻击者通过在目标网站上注入恶意脚本&#xff0c;当用户浏览该网站时&#xff0c;恶意脚本会在用户的浏…

类的函数成员(二):析构函数

一.定义 析构函数(destructor) 与构造函数相反&#xff0c;当对象结束其生命周期&#xff0c;如对象所在的函数已调用完毕时&#xff0c;系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作。 例如&#xff0c;在建立对象时用new开辟了一片内存空间&#xff0c;dele…

单链表就地逆置

算法思想&#xff1a;构建一个带头结点的单链表L&#xff0c;然后访问链表中的每一个数据结点&#xff0c;将访问到的数据结点依此插入到L的头节点之后。 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef s…

Mysql重点思考(上)--mysql的索引优化

mysql的索引优化 expalin关键字的用法explain索引优化示例 type列用法执行查询的顺序类型概述 索引概念索引的定义索引的分类主键&唯一区别 唯一索引的创建和查询创建一个唯一索引查询一个唯一索引 场景题合集唯一索引的场景题主键索引的场景题&#xff08;B树&#xff09;…

蓝桥备赛——矩阵读入

题目描述 如上图所示&#xff0c;是一道有关二维前缀和的问题&#xff0c;因为涉及到二维&#xff0c;肯定就是以矩阵的形式进行读入的。 为此&#xff0c;针对矩阵的读入形式进行总结&#xff0c;可以大致总结出两种类型如下&#xff1a; 二维列表推导式 n, m, k map(int…

HTML教程(详细汇总)

目录 前言&#xff1a; Sublime Text(收费): VS Code(免费): HBuilderX(免费): Dreamweaver(免费): Webstorm(收费): 一.网站的概念&#xff1a; 1.什么是网页&#xff1a; 2.什么是网站&#xff1a; 3.服务器&#xff1a; 总结&#xff1a; 二.HTML简介&#xff…

【JavaEE初阶系列】——CAS

目录 &#x1f388;什么是 CAS &#x1f4dd;CAS 伪代码 &#x1f388;CAS 是怎么实现的 &#x1f388;CAS 有哪些应用 &#x1f6a9;实现原子类 &#x1f308;伪代码实现: &#x1f6a9;实现自旋锁 &#x1f308;自旋锁伪代码 &#x1f388;CAS 的 ABA 问题 &#…

黄金票据制作-新手向

黄金票据制作 文章目录 黄金票据制作0x01 前言0x02 黄金票据的制作一、靶场搭建二、收集制作信息获取域名称获取域SID值获取域用户krbtgt密码hash值 二、制作票据 0x03 验证票据有效性 0x01 前言 最近&#xff0c;我学习了内网渗透的相关知识&#xff0c;其中包括了黄金票据的…

浏览器页面缓存机制

HTTP缓存机制的核心思想是&#xff0c;对于已经请求过的资源&#xff0c;如果其在服务器上没有发生变化&#xff0c;那么浏览器就可以直接从本地缓存中获取这些资源&#xff0c;而无需再次向服务器发送请求。 强缓存 就是确定可用的缓存 浏览器和和服务器对每个缓存资源先商量一…

fread和fwirte函数

✨✨ 欢迎大家来到莉莉的博文✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 一、fread函数 ——>从文件流中读取二进制数据到ptr指向的数组 从流&#xff08;二进制文件&#xff09;中读取数据块 ptr&#xff1a;指向大小至…

基于JSP在线订花系统

基于JSP在线订花系统的设计与实现 摘要 近年来&#xff0c;随着人们对于生活品质的重视度日益提升&#xff0c;鲜花的需求量也在不断增加同时带动了鲜花电商的飞速发展和应用&#xff0c;平台化的鲜花交易模式也逐渐从传统的鲜花销售转型为个性化鲜花定制。同时随着鲜花速递行…

基于 RisingWave 和 ScyllaDB 构建事件驱动应用

概览 在构建事件驱动应用时&#xff0c;人们面临着两大挑战&#xff1a;1&#xff09;低延迟处理大量数据&#xff1b;2&#xff09;实现流数据的实时摄取和转换。 结合 RisingWave 的流处理功能和 ScyllaDB 的高性能 NoSQL 数据库&#xff0c;可为构建事件驱动应用和数据管道…

MTMT:构建比特币生态平行世界 打造铭文生态繁荣

近年来&#xff0c;随着铭文市场的火爆以及比特币ETF成功通过&#xff0c;比特币生态正经历着一场复兴&#xff0c;尤其是铭文市场作为新一代Web3的叙事&#xff0c;带来了全新的生产方式&#xff0c;可以预见&#xff0c;铭文就像流动性挖矿对于上一轮DeFi Summer的推动一样会…

KNN算法 | K近邻:KD Tree、球树、KNN数据下采样策略

目录 一. KNN算法实现方式1. 蛮力实现(brute)2. KD树(kd_tree)3. 球树(ball_tree) 二. KD Tree算法1. 构建方式2. KD Tree 查找最近邻 三. 球树(Ball Tree)1. 构建方式 四. KNN评价1. 优点2. 缺点 五. 延申1. KNN数据下采样策略策略1策略2策略3策略4 Condensed Nearest Neighbo…

Macs Fan Control Pro--精准掌控Mac风扇,优化散热新选择

Macs Fan Control Pro是一款专为Mac电脑设计的高级风扇控制工具。它具备强大的温度监测能力&#xff0c;可以实时监测Mac电脑各个核心组件的温度&#xff0c;并通过直观的界面展示给用户。同时&#xff0c;用户可以根据个人需求自定义风扇速度&#xff0c;或者选择预设的自动风…

蓝桥杯算法题-图形排版

题目描述 小明需要在一篇文档中加入 N 张图片&#xff0c;其中第 i 张图片的宽度是 Wi&#xff0c;高度是 Hi。   假设纸张的宽度是 M&#xff0c;小明使用的文档编辑工具会用以下方式对图片进行自动排版&#xff1a; 1. 该工具会按照图片顺序&#xff0c;在宽度 M 以内&…