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

news2024/11/15 11:00:49

 介绍

本篇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层的原理
  • 各种开发调试工具
  • 智能设备开发
  • 分布式开发等等。

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

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

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

相关文章

数据结构从入门到实战——顺序表

目录 前言 一、顺序表的概念及结构 1.1 线性表 二、顺序表分类 三、动态顺序表的实现 3.1 顺序表结构的创建以及初始化 3.2 顺序表的销毁 3.3 顺序表的打印 3.4 尾插数据 ——最困难的 3.5 头插数据 3.6 尾删数据 3.7 头部删除数据 前言 在计算机科学和数据结…

强大的系统监测工具 iStat Menus for mac最新中文激活版

iStat Menus for Mac是一款功能强大的系统监控工具&#xff0c;专为Mac用户设计&#xff0c;旨在帮助用户全面了解电脑的运行状态&#xff0c;提高电脑的性能和稳定性。 iStat Menus for mac最新中文激活版下载 该软件可以实时监测CPU使用率、内存占用、网络速度、硬盘活动等各…

第九届少儿模特明星盛典 全球赛推广大使『费菲』精彩回顾

2024年1月30日-2月1日&#xff0c;魔都上海迎来了龙年第一场“少儿形体行业美育春晚”&#xff01;由IPA模特委员会主办的第九届少儿模特明星盛典全球总决赛圆满收官&#xff01;近2000名少儿模特选手从五湖四海而来&#xff0c;决战寒假这场高水准&#xff0c;高人气&#xff…

Harmony与Android项目结构对比

主要文件对应 Android文件HarmonyOS文件清单文件AndroidManifest.xmlmodule.json5Activity/Fragmententryability下的ts文件XML布局pages下的ets文件resresourcesModule下的build.gradleModule下的build-profile.json5gradlehvigor根目录下的build.gradle根目录下的build-profi…

pnpm 报错: ERR_PNPM_META_FETCH_FAIL

今天突然遇到一个报错&#xff0c;pnpm 报错&#xff1a; ERR_PNPM_META_FETCH_FAIL  GET https://registry.npm.taobao.org/vue%2Fcli-service: request to https://registry.npm.taobao.org/vue%2Fcli-service failed, reason: certificate has expired问题原因&#xff1a;…

国外EDM邮件群发多少钱?哪个软件好?

在当今全球化市场环境下&#xff0c;电子邮件营销作为最有效的数字营销渠道之一&#xff0c;其影响力不容忽视。而高效精准的EDM&#xff08;Electronic Direct Mail&#xff09;邮件营销策略更是企业拓展海外市场、提升品牌知名度的关键手段。云衔科技以其创新的智能EDM邮件营…

316_C++_xml文件解析成map,可以放到QT表格上, 且 xml、xlsx文件可以互相解析

xml文件例如&#xff1a; <?xml version"1.0" encoding"UTF-8" standalone"yes"?> <TrTable> <tr id"0" label"TR_PB_CH" text"CH%2"/> <tr id"4" label"TR_PB_CHN"…

新专业探路:智能视觉工程专业实验室如何建?

今日&#xff0c;“第八届全国高校电子信息类专业教学论坛”在成都圆满闭幕。整体会议聚焦人才自主培养、一流专业建设、新质课程建设&#xff0c;来自全国多所高校和企业的参会代表齐聚一堂&#xff0c;分享教育与教学成果为推动电子信息类专业建设及课程建设&#xff0c;探讨…

指针的深入理解(七)

指针的深入理解&#xff08;七&#xff09; 个人主页&#xff1a;大白的编程日记 个人专栏&#xff1a;C语言学习之路 感谢遇见&#xff0c;我们一起学习进步&#xff01; 文章目录 指针的深入理解&#xff08;七&#xff09;前言一.常量字符串指针1.1常量字符串的理解1.2常量…

UDTF函数 explode

场景&#xff1a; 原hive数据形式 split 处理到一个Array 形式 使用explode炸开后的效果是 explode结合侧面视图达到targeType 目标形式&#xff1a; 一进多出 explode 将hive 中复杂的 array 炸成多行 因为炸开后&#xff0c; movie 列值少于categoryname 列所以这里为了达到…

如何使用Python曲线拟合

在Python中进行曲线拟合通常涉及使用科学计算库&#xff08;如NumPy、SciPy&#xff09;和绘图库&#xff08;如Matplotlib&#xff09;。下面是一个简单的例子&#xff0c;演示如何使用多项式进行曲线拟合&#xff0c;在做项目前首先&#xff0c;确保你已经安装了所需的库。 1…

宝宝洗衣机怎么选?四款畅销卓越婴儿洗衣机深度剖析!

近几年科技高速发展&#xff0c;我们的生活也因此变得更加便捷、健康高效。尤其是在家庭生活中&#xff0c;各种新兴家电的出现让我们的生活变得更加健康卫生。婴儿洗衣机也为现代家庭提供了极大的便捷。由于婴儿刚出生免疫力比较弱&#xff0c;所以建议婴儿的衣物尽量和大人的…

10分钟1000台虚机 云安全效能双升 亚信安全新信舱无代理云平台快速适配版正式发布

新信舱 亚信安全新信舱无代理云平台快速适配版正式发布。在云平台依赖性、无代理部署速度、宿主机无代理AV防护和虚拟机缓存扫描性能等方面&#xff0c;新信舱无代理版本提供了无缝的可扩展性、低资源消耗并降低管理复杂性&#xff0c;让安全防护真正做到了 多快好省&#xff…

Spring Boot 整合 Mockito:提升Java单元测试的高效实践

引言 在Java开发领域&#xff0c;Spring Boot因其便捷的配置和强大的功能而受到广泛欢迎&#xff0c;而Mockito作为一款成熟的单元测试模拟框架&#xff0c;则在提高测试质量、确保代码模块间解耦方面扮演着至关重要的角色。本文将详细介绍如何在Spring Boot项目中整合Mockito&…

天府锋巢运营方树莓集团——全国园区赋能,助力企业开源节流

树莓集团&#xff0c;作为天府锋巢直播产业基地的运营方&#xff0c;一直在数字产业生态链的建设中走在行业前列。不同于传统的园区或商管公司&#xff0c;树莓集团不仅为企业提供物理入驻空间&#xff0c;更是创造了高效、多维度、多渠道的无边界产业办公体验。这种独特的运营…

第四百五十四回

文章目录 1. 问题描述2. 优化方法2.1 缩小范围2.2 替代方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取AppBar的高度"相关的内容&#xff0c;本章回中将介绍关于MediaQuery的优化.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 问题描述 我们在…

package.java文件的作用

你查看springboot的源码&#xff0c;有很多类都有这个文件&#xff0c;在idea不能创建&#xff0c;因为不支持这种命名&#xff0c;只能用记事本创建后复制都项目中。 主要应用是给类添加正常&#xff0c;或者把公用的注解都放到这里&#xff0c;常量不合适&#xff0c;作用范…

爬虫机试题-爬取新闻网站

之前投简历时遇到了这样的一个笔试。本以为会是数据结构算法之类的没想到直接发了一个word直接提需求&#xff0c;感觉挺有意思就写了这篇文章&#xff0c;感兴趣的朋友可以看看。 拿到urllist 通过分析页面结构我们得以知道&#xff0c;这个页面本身没有新闻信息&#xff0c;是…

夜月一帘幽梦,春风十里“三指针法“ (链表面试题篇2)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

Gemini国内怎么使用

GPT、Claude、Gemini全系列模型国内使用方法来了&#xff01; 一直以来很多人问我能不能有个稳定&#xff0c;不折腾的全球AI大模型测试网站&#xff0c;既能够保证真实靠谱&#xff0c;又能够保证稳定、快速&#xff0c;不要老动不动就挂了、出错或者漫长的响应。 到目前为止…