OpenHarmony开发案例:【电影卡片】

 介绍

本篇Codelab基于元服务卡片的能力,实现带有卡片的电影应用,介绍卡片的开发过程和生命周期实现。需要完成以下功能:

  1. 元服务卡片,用于在桌面上添加2x2或2x4规格元服务卡片。
  2. 关系型数据库,用于创建、查询、添加、删除卡片数据。

相关概念

  • [关系型数据库]:关系型数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。

  • [元服务卡片]:卡片是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。

    • 卡片提供方:显示卡片内容,控制卡片布局以及控件点击事件。
    • 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
    • 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。
  • 鸿蒙开发参考文档:qr23.cn/AKFP8k点击或者复制转到。

硬件要求

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:3.2 Release。

环境搭建

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

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。

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

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──entry/src/main/ets            // 代码区     
│  ├──common  
│  │  ├──constants
│  │  │  ├──CommonConstants.ets  // 常量类
│  │  │  └──StyleConstants.ets   // 格式常量类
│  │  ├──datasource
│  │  │  ├──DataSource.ets       // 懒加载数据源
│  │  │  └──MovieListData.ets    // 电影列表数据 
│  │  └──utils
│  │     ├──CommonUtils.ets      // 数据操作工具类  
│  │     ├──GlobalContext.ets    // 全局上下文工具类
│  │     └──Logger.ets           // 日志打印工具类
│  ├──detailsability
│  │  └──EntryDetailsAbility.ets // 电影详情入口类
│  ├──entryability
│  │  └──EntryAbility.ets        // 程序入口类
│  ├──entryformability
│  │  └──EntryFormAbility.ets    // 卡片创建,更新,删除操作类
│  ├──pages
│  │  ├──MovieDetailsPage.ets    // 电影详情页
│  │  └──MovieListPage.ets       // 主页面
│  ├──view
│  │  ├──MovieDetailsTitle.ets   // 电影详情头部组件
│  │  ├──MovieItem.ets           // 列表item组件
│  │  ├──MovieStarring.ets       // 电影主演组件
│  │  ├──MovieStills.ets         // 电影剧照组件
│  │  ├──StarsWidget.ets         // 电影评分组件
│  │  └──StoryIntroduce.ets      // 电影简介组件
│  └──viewmodel
│     ├──FormBean.ets            // 卡片对象
│     ├──FormDataBean.ets        // 卡片数据对象
│     └──MovieDataBean.ets       // 电影数据对象
├──entry/src/main/js             // js代码区
│  ├──card2x2                    // 2x2卡片目录
│  ├──card2x4                    // 2x4卡片目录
│  └──common                     // 卡片资源目录
└──entry/src/main/resources      // 资源文件目录

搜狗高速浏览器截图20240326151344.png

关系型数据库

元服务卡片需要用数据库保存不同卡片数据,而且在添加多张卡片情况下,需要保持数据同步刷新。因此需要创建一张表,用于保存卡片信息。

  1. 数据库创建使用的SQLite。

    // CommonConstants.ets
    // 创建数据库表结构
    static readonly CREATE_TABLE_FORM: string = 'CREATE TABLE IF NOT EXISTS Form ' +
      '(id INTEGER PRIMARY KEY AUTOINCREMENT, formId TEXT NOT NULL, formName TEXT NOT NULL, dimension INTEGER)';

  2. 在EntryAbility的onCreate方法通过CommonUtils.createRdbStore方法创建数据库,并创建相应的表。

    // EntryAbility.ets
    export default class EntryAbility extends UIAbility {
      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
        ...
        // 创建数据库
        CommonUtil.createRdbStore(this.context);
      }
    }
    
    // CommonUtils.ets
    import relationalStore from '@ohos.data.relationalStore';
    
    async createRdbStore(context: Context) {
      let rdbStore = GlobalContext.getContext().getObject('rdbStore') as relationalStore.RdbStore;
      if (this.isEmpty(rdbStore)) {
        rdbStore = await relationalStore.getRdbStore(context, CommonConstants.STORE_CONFIG);
        if (!this.isEmpty(rdbStore)) {
          rdbStore.executeSql(CommonConstants.CREATE_TABLE_FORM).catch((error: Error) => {
            Logger.error(CommonConstants.TAG_COMMON_UTILS, 'executeSql error ' + JSON.stringify(error));
          });
          GlobalContext.getContext().setObject('rdbStore', rdbStore);
        }
      }
      return rdbStore;
    }

构建应用页面

电影卡片应用有两个页面,分别是电影列表和电影详情。

电影列表

电影列表采用Column容器嵌套List和自定义组件MovieItem形式完成页面整体布局,效果如图所示:

 
// MovieListPage.ets
build() {
  Column() {
    ...
    List({ space: StyleConstants.LIST_COMPONENT_SPACE }) {
      LazyForEach(this.dataSource, (item: MovieDataBean) => {
        ListItem() {
          // 电影item
          MovieItem({ movieItem: item });
        }
      }, (item: MovieDataBean) => JSON.stringify(item))
    }
    ...
  }
  ...
}

// MovieItem.ets
aboutToAppear() {
  if (CommonUtils.isEmpty(this.movieItem)) {
    Logger.error(CommonConstants.TAG_MOVIE_ITEM, 'movieItem is null');
    return;
  }
  // 获取电影索引
  this.sort = this.movieItem.sort;
  ...
}

build() {
  Row(){
    ...
    Text($r('app.string.want_to_see'))
      ...
      .onClick(() => {
        router.pushUrl({
          url: CommonConstants.SEE_BUTTON_PUSH,
          params: {
            index: this.sort
          }
        }).catch((error: Error) => {
          ...
        });
      })
  }
  ...
}

电影详情

电影详情采用Column容器嵌套自定义组件MovieDetailsTitle、StoryIntroduce、MovieStarring和MovieStills形式完成页面整体布局,效果如图所示:

 
// MovieDetailPage.ets
aboutToAppear() {
   let index: number = 0;
   let params = router.getParams() as Record<string, Object>;
   if (!CommonUtils.isEmpty(params)) {
      index = params.index as number;
   } else {
      let position = GlobalContext.getContext().getObject('position') as number;
      index = position ?? 0;
   }
   let listData: MovieDataBean[] = CommonUtils.getListData();
   if (CommonUtils.isEmptyArr(listData)) {
      Logger.error(CommonConstants.TAG_DETAILS_PAGE, 'listData is 0');
      return;
   }
   this.movieData = listData[index];
   this.introduction = listData[index].introduction;
}

build() {
  Column() {
    ...
    Column() {
      // 电影详情头部组件
      MovieDetailsTitle({
        movieDetail: this.movieData
      })
      // 剧情简介组件
      StoryIntroduce({
        introduction: this.introduction
      })
    }
    ...
    // 电影主演组件
    MovieStarring()
    // 电影剧照组件
    MovieStills()
  }
  ...
}

元服务卡片

使用元服务卡片分为四步:创建、初始化、更新、删除。

创建元服务卡片目录

  1. 在main目录下,点击鼠标右键 > New > Service Widget。

  2. 然后选择第一个选项下面带有Hello World字样,点击下一步Next。

  3. 填写卡片名字(Service widget name)、卡片介绍(Description)、是否开启低代码开发(Enable Super Visual)、开发语言(ArkTS和JS)、支持卡片规格(Support dimension)、关联表单(Ability name)点击Finish完成创建。如需创建多个卡片目录重新按照步骤1执行。

  4. 创建完卡片后,同级目录出现js目录,然后开发者在js目录下使用hml+css+json开发js卡片页面。

初始化元服务卡片

应用选择添加元服务卡片到桌面后,在EntryFormAbility的onAddForm方法进行卡片初始化操作,效果如图所示:

 
// EntryFormAbility.ets
onAddForm(want: Want) {
   if (want.parameters === undefined) {
      return formBindingData.createFormBindingData();
   }
   let formId: string = want.parameters[CommonConstants.IDENTITY_KEY] as string;
   let formName: string = want.parameters[CommonConstants.NAME_KEY] as string;
   let dimensionFlag: number = want.parameters[CommonConstants.DIMENSION_KEY] as number;
   CommonUtils.createRdbStore(this.context).then((rdbStore: relationalStore.RdbStore) => {
      let form: FormBean = new FormBean();
      form.formId = formId;
      form.formName = formName;
      form.dimension = dimensionFlag;
      CommonUtils.insertForm(form, rdbStore);
   }).catch((error: Error) => {
      Logger.error(CommonConstants.TAG_FORM_ABILITY, 'onAddForm create rdb error ' + JSON.stringify(error));
   });
   let listData: MovieDataBean[] = CommonUtils.getListData();
   let formData = CommonUtils.getFormData(listData);
   return formBindingData.createFormBindingData(formData);
}

更新元服务卡片

  1. 初始化加载电影列表布局之前,在MovieListPage的aboutToAppear方法中,通过CommonUtils.startTimer方法开启定时器,时间到则调用updateMovieCardData方法更新电影卡片数据。

    // MovieListPage.ets
    aboutToAppear() {
      ...
      // 启动定时器,每5分钟更新一次电影卡片数据。
      CommonUtils.startTimer();
    }
    
    // CommonUtils.ets
    startTimer() {
      let intervalId = GlobalContext.getContext().getObject('intervalId') as number;
      if (this.isEmpty(intervalId)) {
        intervalId = setInterval(() => {
          let rdbStore = GlobalContext.getContext().getObject('rdbStore') as relationalStore.RdbStore;
          this.updateMovieCardData(rdbStore);
        }, CommonConstants.INTERVAL_DELAY_TIME);
      }
      GlobalContext.getContext().setObject('intervalId', intervalId);
    }
    
    // 更新电影卡片数据
    updateMovieCardData(rdbStore: relationalStore.RdbStore) {
     if (this.isEmpty(rdbStore)) {
       Logger.error(CommonConstants.TAG_COMMON_UTILS, 'rdbStore is null');
       return;
     }
     let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(CommonConstants.TABLE_NAME);
     rdbStore.query(predicates).then((resultSet: relationalStore.ResultSet) => {
       if (resultSet.rowCount <= 0) {
         Logger.error(CommonConstants.TAG_COMMON_UTILS, 'updateCardMovieData rowCount <= 0');
         return;
       }
       let listData: MovieDataBean[] = this.getListData();
       resultSet.goToFirstRow();
       do {
         let formData = this.getFormData(listData);
         let formId: string = resultSet.getString(resultSet.getColumnIndex(CommonConstants.FORM_ID));
         formProvider.updateForm(formId, formBindingData.createFormBindingData(formData))
           .catch((error: Error) => {
             Logger.error(CommonConstants.TAG_COMMON_UTILS, 'updateForm error ' + JSON.stringify(error));
           });
       } while (resultSet.goToNextRow());
       resultSet.close();
     }).catch((error: Error) => {
       Logger.error(CommonConstants.TAG_COMMON_UTILS, 'updateCardMovieData error ' + JSON.stringify(error));
     });

  2. 通过src/main/resources/base/profile/form_config.json配置文件,根据updateDuration或者scheduledUpdateTime字段配置刷新时间。updateDuration优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。当配置的刷新时间到了,系统调用onUpdateForm方法进行更新。

    // form_config.json
    {
      // 卡片的类名
      "name": "card2x2",
      // 卡片的描述
      "description": "This is a service widget.",
      // 卡片对应完整路径 
      "src": "./js/card2x2/pages/index/index",
      // 定义与显示窗口相关的配置
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      // 卡片的主题样式
      "colorMode": "auto",
      // 是否为默认卡片
      "isDefault": true,
      // 卡片是否支持周期性刷新
      "updateEnabled": true,
      // 采用24小时制,精确到分钟
      "scheduledUpdateTime": "00:00",
      // 当取值为0时,表示该参数不生效,当取值为正整数N时,表示刷新周期为30*N分钟。
      "updateDuration": 1,
      // 卡片默认外观规格
      "defaultDimension": "2*2",
      // 卡片支持外观规格
      "supportDimensions": [
        "2*2"
      ]
    }
    ...
    
    // EntryFormAbility.ets
    onUpdateForm(formId: string) {
      CommonUtils.createRdbStore(this.context).then((rdbStore: relationalStore.RdbStore) => {
        CommonUtils.updateMovieCardData(rdbStore);
      }).catch((error: Error) => {
        ...
      });
      ...
    }

删除元服务卡片

当用户需要删除元服务卡片时,可以在EntryFormAbility的onRemoveForm方法中,通过CommonUtils.deleteFormData方法删除数据库中对应的卡片信息。

// EntryFormAbility.ets
onRemoveForm(formId: string) {
  CommonUtils.createRdbStore(this.context).then((rdbStore: relationalStore.RdbStore) => {
    // 从数据库中删除电影卡片信息
    CommonUtils.deleteFormData(formId, rdbStore);
  }).catch((error: Error) => {
    ...
  });
}

// CommonUtils.ets
deleteFormData(formId: string, rdbStore: relationalStore.RdbStore) {
  ...
  let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(CommonConstants.TABLE_NAME);
  predicates.equalTo(CommonConstants.FORM_ID, formId);
  rdbStore.delete(predicates).catch((error: Error) => {
    ...
  });
}

鸿蒙语言有TS、ArkTS等语法,那么除了这些基础知识之外,其核心技术点有那些呢?下面就用一张整理出的鸿蒙学习路线图表示:

从上面的OpenHarmony技术梳理来看,鸿蒙的学习内容也是很多的。现在全网的鸿蒙学习文档也是非常的少,下面推荐一些:完整内容可在头像页保存,或这qr23.cn/AKFP8k甲助力

内容包含:《鸿蒙NEXT星河版开发学习文档》

  • ArkTS
  • 声明式ArkUI
  • 多媒体
  • 通信问题
  • 系统移植
  • 系统裁剪
  • FW层的原理
  • 各种开发调试工具
  • 智能设备开发
  • 分布式开发等等。

这些就是对往后开发者的分享,希望大家多多点赞关注喔!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端知识学习笔记-五(ECMAScript 6)

命令行工具 在正式讲解ES6新特性之前&#xff0c;我们需要了解一些命令行工具&#xff0c;在日后的课程中&#xff0c;我们会经常用到命令行 常用命令行工具有两种 CMD 命令行工具 PowerShe门命令行工具 CMD命令行 打开命令行窗口 win: 左下角开始&#xff0c;找到运行&#…

IntelliJ IDEA(WebStorm、PyCharm、DataGrip等)设置中英文等宽字体,英文为中文的一半(包括标点符号)

1.设置前&#xff08;idea默认字体为 JetBrains Mono&#xff09; 2.设置后&#xff08;楷体&#xff09;

HIT The Wiorld,HIT世界官网地址+配置要求+测试时间+加速器分享

HIT The Wiorld&#xff0c;HIT世界官网地址配置要求测试时间加速器分享 NEXON新游《HIT&#xff1a;世界&#xff08;HIT&#xff1a;The World&#xff09;》将在4月17日上线&#xff0c;目前已在官网开启事前预约预创建角色。Hit :the world&#xff08;HIT:世界&#xff…

苹果个人证书管理

根据近日工业和信息化部发布的《工业和信息化部关于开展移动互联网应用程序备案工作的通知》&#xff0c;相信不少要进行IOS平台App备案的朋友遇到了一个问题&#xff0c;就是apple不提供云管理式证书的下载&#xff0c;也就无法获取公钥及证书SHA-1指纹。 已经上架的应用不想重…

如何在浏览器Web前端在线编辑PPT幻灯片?

有时候在项目中我们会遇到需要在网页在线打开并编辑PPT文档保存到本地或者服务器指定位置&#xff0c;猿大师办公助手可以很方便的调用本机Office实现在网页上编辑PPT幻灯片&#xff0c;效果与本机Office打开PPT完全一样。 猿大师办公助手支持完整嵌入模式&#xff0c;也就是本…

React-样式使用

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-样式使用 目录 1、行内样式 2、使用className属性 3、css module模块化 4、styled-c…

基于JSP本科生毕业设计选题系统的设计与实现(内附设计LW + PPT+ 源码下载)

基于JSP本科生毕业设计选题系统的设计与实现 项目名称&#xff1a; 基于JSP本科生毕业设计选题系统的设计与实现 项目技术栈 该项目采用了以下核心技术栈&#xff1a; 后端框架/库&#xff1a; SSM框架&#xff08;Spring MVC、Spring、Mybatis&#xff09;数据库&#xff…

OpenHarmony轻量系统开发【3】代码编译和烧录

3.1源码目录 下载完代码后&#xff0c;大家可以进入代码目录&#xff1a; 这里重点介绍几个比较重要的文件夹&#xff1a; 1 vendor文件夹 该文件夹存放的是厂商相关的配置&#xff0c;包括组件配置、HDF相关配置&#xff0c;代码目录如下&#xff1a; 可以看到有hisilicon文…

IDEA pom.xml显示灰色并被划线

在使用 IDEA 进行开发的过程中&#xff0c;有时候会遇到 pom.xml 显示灰色并被划线的情况&#xff0c;如下图&#xff1a; 这一般是因为该文件被 Maven 忽略导致的&#xff0c;可以进行如下操作恢复&#xff1a; 设置保存后&#xff0c;可以看到 pom.xml 恢复了正常&#xff1a…

github,raw.githubusercontent.com 等网址登陆不上不去的设置方法

目录 提示域名解析错误&#xff1a; 出现的现象&#xff1a; 解决办法&#xff1a;修改host host改完不生效 解决方案1&#xff1a; 解决方案2&#xff1a; 提示域名解析错误&#xff1a; 出现的现象&#xff1a; 登陆github&#xff0c;raw.githubusercontent.com 等网…

解读《算者生存:商业分析的方法与实践》:构建企业经营分析框架的必备指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

LOCK、ACC、ON、START的含义及正确使用

背景 前段时间在开发一个远程锁车的需求时&#xff0c;讨论到了电源状态的场景。由于初次进入汽车电子行业&#xff0c;对很多基础概念不清晰。当时听主机厂商的同事介绍一遍后&#xff0c;并不是很理解。于是趁着空闲&#xff0c;给自己充充电&#xff0c;也希望能够帮到有需…

前端打包webpack vite

起步 | webpack 中文文档 | webpack中文文档 | webpack中文网 npm run build 1webpack: mkdir webpack-demo cd webpack-demo npm init -y npm install webpack webpack-cli --save-dev vite : 快速上手 | Vue.js

Netty学习——实战篇2 NIO 群聊系统(简单版) 备份

需求&#xff1a; 1、编写一个NIO群聊系统&#xff0c;实现服务端和客户端之间数据简单通讯(非阻塞) 2、实现多人群聊 3、服务端&#xff1a;可以监测用户上线、离线、并实现消息转发功能。 4、客户端&#xff1a;通过channel可以无阻塞发送消息给其他所有用户&#xff0c;同时…

【位运算】3097. 或值至少为 K 的最短子数组 II

本文涉及知识点 位运算 LeetCode3097. 或值至少为 K 的最短子数组 II 给你一个 非负 整数数组 nums 和一个整数 k 。 如果一个数组中所有元素的按位或运算 OR 的值 至少 为 k &#xff0c;那么我们称这个数组是 特别的 。 请你返回 nums 中 最短特别非空 子数组 的长度&…

AI大模型语言开源大语言模型完整列表

开源大语言模型完整列表 Large Language Model (LLM) 即大规模语言模型&#xff0c;是一种基于深度学习的自然语言处理模型&#xff0c;它能够学习到自然语言的语法和语义&#xff0c;从而可以生成人类可读的文本。 所谓"语言模型"&#xff0c;就是只用来处理语言文…

游戏开发者必看:Perforce Helix Core 的功能特点及游戏开发中的常用工具、典型用例介绍

「不出海&#xff0c;即出局」随着全球化的加速发展&#xff0c;企业出海已成燎原之势。日前&#xff0c;2024 亚马逊云科技出海全球化论坛在深圳成功举办。龙智携手 Perforce 亮相游戏行业展区&#xff0c;展示了Perforce Helix Core如何与主流游戏开发引擎高效集成&#xff0…

关于《CS创世 SD NAND》的技术学习分享

最近发现一个好玩的东西《CS创世 SD NAND》&#xff0c;带大家一起体验一下。 本文引用了部分厂家产品资料及图像&#xff0c;如有侵权&#xff0c;请及时联系我删除&#xff0c;谢谢。 《CS创世 SD NAND》官方网站&#xff1a;http://www.longsto.com/ 什么是CS创世 SD NAND呢…

c++的学习之路:4、入门(3)

摘要 本章将介绍一下auto、for和指针空值&#xff0c;文章末附上入门的所有代码。 目录 摘要 一、auto 二、for 三、指针空值 四、代码 五、思维导图 一、auto 这个关键字是c提出的&#xff0c;可以自动识别变量的类型&#xff0c;可以看出下方图片&#xff0c;auto自…

【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩

文章目录 背景介绍 初始代码 优化代码 分析和应用 总结 背景介绍 在一个嵌入式软件开发项目中&#xff0c;有一个使用MATLAB Function编写的算法模块&#xff0c;功能是从一个较大的数组中提取一段数据&#xff0c;然后求均值输出&#xff0c;示例如下&#xff1a; 初始代…