深度分析HarmonyOS开辟-运动召集令元服务【鸿蒙北向应用开辟实战】 ...

打印 上一主题 下一主题

主题 809|帖子 809|积分 2437

随着HarmonyOS NEXT崭露头角,越来越多的公司开始探索鸿蒙应用开辟。虽然目前HarmonyOS NEXT尚未向个人开辟者开放,但我们可以体验并利用最新的API9和开辟工具,欢迎将来,体验鸿蒙新的应用形态——元服务。
元服务,作为HarmonyOS提供的创新服务情势,具备独立入口、免安装的特性,支持多种呈现方式,如灵活的卡片式展示等。本文将深入分析元服务的核心概念,先容基于最新API9的开辟流程,以及利用的开辟工具,为读者提供全方位的元服务开辟体验。
本文将为您展现将来鸿蒙生态的精彩面目。跟随我们一起踏上HarmonyOS的元服务开辟之旅,体验鸿蒙的创新气力!
本文将基于运动召集令元服务的开辟案例带大家一起学习:
元服务内部功能:

  • 提供页面供用户创建一个集结运动,如周末轰趴,输入时间,地点,人数,等运动信息
  • 提供一个列表页面,供浏览所有公开运动
  • 登录用户可以查看运动详情,可以报名到场
元服务卡片:

  • 卡片界面展示所有运动列表。
  • 卡片界面展示最新未报名的运动列表,支持直接在卡片上报名运动。
项目实现演示效果如下:

     运动召集令元服务视频
  
一.元服务和ArkTS语言简介

元服务 (原为原子化服务) 是一种基于HarmonyOS API的全新服务提供方式,元服务仅需开辟一次,便可以运行在多种范例的终端设备上,以鸿蒙万能卡片等多种呈现形态, 向用户提供更轻量化的服务。
鸿蒙万能卡片是元服务最重要的呈现形态之一(其他形态如语音、图标等),每一个万能卡片都是在桌面上“永远打开的”元服务/应用,将元服务/应用的重要信息以卡片的情势展示在桌面,通过轻量交互行为实现服务直达。
1.1 学习元服务

在万物互联期间,人均持有设备量不停攀升,设备和场景的多样性,使服务开辟变得更加复杂、服务入口更加丰富。在此趋势下,应用提供方和用户迫切需要一种新的服务提供方式,使应用开辟更简单、服务的获取和利用更便捷。为此,HarmonyOS 提供了基于元服务和应用的便捷服务。
元服务是 HarmonyOS 提供的一种面向将来的服务提供方式,是有独立入口的(用户可通过点击、碰一碰、扫一扫等方式直打仗发)、免安装的(无需显式安装,由体系步伐框架后台安装后即可利用)、可为用户提供一个或多个便捷服务的用户步伐形态。元服务基于 HarmonyOS API 开辟,支持运行在 1+8+N 设备上,供用户在符合的场景、符合的设备上便捷利用。
元服务具有随处可及、服务免安装直达、分布式流转等特性。日常生存中,用户可以通过扫描 HarmonyOS Connect 标签、“碰一碰”设备来快速启动元服务,也可以在设备的服务中心和桌面上轻松找到他。

1.2 元服务带来的变革

(1) 免安装,更轻量化地将服务带给用户
(2) 一键服务直达,将用户感兴趣的内容前置、外显
(3) 跨端转移,多终端设备间无缝流转
(4) 景象智能卡片推荐,随心定制、更懂用户
1.3 元服务全场景流量入口


  • 应用市场
    用户可以点击应用市场-应用页-元服务入口体验元服务。
  • 负一屏/服务中心
    点击即可快速直达对应服务页面,用户还可以自主在常驻服务栏设置喜爱或常用的服务。
  • 桌面
    用户可以将服务添加到桌面,随时随地查看信息、获取服务。
  • 智慧搜刮
    用户在搜刮到相关关键词时,关联的元服务则会出现在搜刮结果中,点击后可以跳转到服务详情页面。
  • 小艺发起
    通过桌面打开小艺发起发现服务卡片,点击服务卡片触发元服务。
  • 小艺语音
    通过语音叫醒词,如“小艺小艺”进入智慧语音,利用语音对话方式触发元服务。
  • 碰扫感知
    用户拿出华为手机“碰一碰”或“扫一扫”,可辨认其他设备上的NFC标签,手机页面将自动跳转到相应的元服务内容,用户在手机侧即可操纵设备。
1.4 ArkTS学习

ArkTS是鸿蒙生态的应用开辟语言。它在保持TypeScript(简称TS)根本语法风格的基础上,对TS的动态范例特性施加更严格的约束,引入静态范例。同时,提供了声明式UI、状态管理等相应的能力,让开辟者可以以更简洁、更天然的方式开辟高性能应用。
同时,提供了声明式UI、状态管理等相应的能力,让开辟者可以以更简洁、更天然的方式开辟高性能应用。
面向万物互联期间,华为提出一次开辟多端摆设、可分可合自由流转、同一生态原生智能三大应用与服务开辟理念,针对多设备、多入口、服务可分可合等特性,提供多种能力协助开辟者降低开辟门槛,同时HarmonyOS与OpenHarmony同一生态。HarmonyOS基于JS/TS语言体系,构建了全新的声明式开辟语言ArkTS。除了兼容JS/TS语言生态,ArkTS扩展了声明式UI语法和轻量化并发机制。
1.5 ArkTS特点



  • 天然简洁语法
    ArkTS提供了简洁天然的声明式语法、组件化机制、数据-UI自动关联等能力,实现了贴近天然语言,书写服从更高的编程方式,为开辟者带来易学、易懂、极简开辟的优质体验。
  • 轻量化并发机制
    ArkCompiler运行时在HarmonyOS上提供了Worker API支持并发编程。在运行时实例内存隔离的基础上,ArkCompiler通过共享运行实例中的不可变或者不易变的对象、内建代码块、方法字节码等技术本领,优化了并发运行实例的启动性能和内存开销。

二.DevEco Studio开辟工具

DevEco Studio是面向全场景多设备,提供一站式的分布式应用开辟平台,支持分布式多端开辟、分布式多端调测、多端模拟仿真,全方位的质量与安全保障。
2.1 DevEco Studio学习

DevEco Studio是一个功能强盛的移动应用开辟工具,基于IntelliJ IDEA Community开源版本打造,面向华为终端全场景多设备的一站式集成开辟情况(IDE),为开辟者提供工程模板创建、开辟、编译、调试、发布等端到端的HarmonyOS应用开辟服务。
2.2 DevEco Studio的重要特性

DevEco Studio的重要特性包括:

  • 工程模板创建:提供多种工程模板,帮助开辟者快速创建项目。
  • 界面可视化编辑:支持通过可视化结构编辑器构建界面,简化了界面开辟过程。
  • 开辟:提供代码提示、语法高亮等功能,帮助开辟者高效编写代码。
  • 编译:支持编译构建,并根据指定的Compile API版本进行编译打包。
  • 调试:提供调试功能,帮助开辟者快速定位和解决问题。
  • 上架:提供应用上架功能,帮助开辟者将应用发布到华为应用市场等平台。
2.3 端云一体化开辟

DevEco Studio的端云一体化开辟是指通过集成AppGallery Connect的认证服务和云函数服务,开辟者可以在创建工程时选择云开辟模板,实现端云一体化协同开辟。这使得开辟者可以在DevEco Studio中进行认证服务的集成,以及云函数的开辟和管理。
为丰富HarmonyOS对云端开辟的支持、实现端云联动,DevEco Studio推出了云开辟功能,开辟者在创建工程时选择云开辟模板,即可在DevEco Studio内同时完成HarmonyOS应用/元服务的端侧与云侧开辟,体验端云一体化协同开辟。
2.3.1端云一体化开辟特点

DevEco Studio的端云一体化开辟包括以下方面:

  • 认证服务集成:开辟者可以在DevEco Studio中集成AppGallery Connect的认证服务,以实现应用步伐的登录入口。通过利用端云一体化的认证服务,开辟者可以方便地实现用户的注册、登录和授权等功能。
  • 云函数开辟:DevEco Studio集成了AppGallery Connect的Serverless云服务开放接口,开辟者可以在创建工程时选择云开辟模板,并通过云函数进行业务逻辑的处理和数据的存储。这使得开辟者可以更加专注于业务逻辑的实现,而不需要关心底层的技术细节。
  • 端云数据同步:通过端云一体化开辟,开辟者可以实现端云数据同步,保证数据的一致性和及时性。例如,当用户在应用步伐中进行操纵时,相关的数据可以被及时同步到云端,从而保证数据的及时性和一致性。
  • 端云协同调试:DevEco Studio支持端云协同调试功能,开辟者可以在开辟过程中进行本地调试和远程调试。当应用步伐在本地进行调试时,开辟者可以通过DevEco Studio与远程设备进行通信,从而进行远程调试。这使得开辟者可以更加方便地进行应用步伐的调试和优化。
相比于传统开辟模式,云开辟模式具备本钱低、服从高、门槛低等优势,具体区别见下表。

2.4 低代码开辟

DevEco Studio的低代码开辟是指通过利用预界说的组件和模板,以及可视化的界面编辑方式,开辟者可以以较少的编码工作量快速构建应用步伐。低代码开辟可以大大提高开辟服从,降低开辟门槛,使非专业的步伐员也能进行应用步伐的开辟。
2.4.1 开辟方式

利用低代码开辟应用或服务有以下两种开辟方式:


  • 创建一个支持低代码开辟的新工程,开辟应用或服务的UI界面。
  • 在已有工程中,创建Visual文件来开辟应用或服务的UI界面。

2.4.2 支持低代码开辟的工程

项目构成目次如下:

2.4.3 创建Visual文件构建UI

在已有的HarmonyOS工程中,可以通过创建Visual文件的方式,利用低代码开辟应用或服务的UI界面,要求compileSdkVersion必须为7或以上。ArkTS低代码要求compileSdkVersion必须为8或以上。
在打开的工程中,选中模块的pages文件夹,单击鼠标右键,选择New > Visual > Page。

三.运动召集令元服务开辟实战

3.1案例背景

在当今繁忙而快节奏的生存中,人们渴望有更多机会聚在一起,共度美好时光。然而,组织和到场运动每每面临着诸多挑战,例如信息不透明、难以找到符合的运动,以及运动宣传不足。为相识决这些问题,我们开辟了一款全新的创意应用——运动召集令元服务。
在城市化进程加速的今天,越来越多的人发现,社交运动是缓解生存压力、创建社交网络以及增进人际关系的重要途径。然而,传统的社交方式难以满足现代人的需求。于是,我萌生了为用户提供一个便捷、高效、社交化的运动发布平台的创意。
在寻找运动信息的过程中,用户经常面临信息碎片化和不一致的情况。有些运动信息散落在不同的社交媒体或运动平台上,用户需要泯灭大量时间和精力来收集和筛选。我们意识到,整合和同一这些信息对提高用户体验至关重要。
随着移动互联技术的迅速发展,我们看到了整合社交元素和运动信息的机会。通过开辟一款创新的应用,用户可以轻松创建、查找和到场各种社交运动。这个应用将成为人们社交生存的数字化扩展,使运动的组织和到场变得更加简单而愉快。
在这个创意应用的背后,我们追求的是毗连人与人之间的纽带,让社交运动更加简单而有趣。我们信任,通过这个运动发布平台,人们可以更轻松地发现、创建和到场各种精彩运动,为生存增加更多色彩。
3.2 创建项目-前期准备

如下图,注册AppGallery Connect并配置相应的配置。进入我的元服务创建项目。

创建应用,填写对应的相关配置。

到DevEco Studio创建新的项目工程。

创建项目之后就可以进行开辟,情况如图。

3.3元服务内部功能详解

3.3.1 登录页面


核心代码如下:
  1. build() {
  2.   Row() {
  3.     Column({space:17}) {
  4.       Image($r("app.media.logo")).width(80)
  5.       Text("活动召集令")
  6.       TextInput({ placeholder: '输入用户名' })
  7.         .width(300)
  8.         .height(60)
  9.         .fontSize(20)
  10.         .onChange((value: string) => {
  11.           this.username = value
  12.         })
  13.       TextInput({ placeholder: '输入密码' })
  14.         .width(300)
  15.         .height(60)
  16.         .fontSize(20)
  17.         .type(InputType.Password)
  18.         .onChange((value: string) => {
  19.           this.password = value
  20.         })
  21.       Button('登录')
  22.         .width(300)
  23.         .height(60)
  24.         .fontSize(20)
  25.         .backgroundColor('#0F40F5')
  26.         .onClick(() => {
  27.           this.S_login();
  28.         })
  29.     }
  30.     .width('100%')
  31.   }
  32.   .height('100%')
  33. }
复制代码

  • @Component是用来界说组件的装饰器,@Entry是页面入口组件。
  • @State 被用来界说组件内部的状态。在这里,activitiesusername`和`password`都是组件的状态。当被@State`修饰的变量发生变革,页面会立即刷新。
  • S_login 方法用于处理登录逻辑。如果用户名和密码是"admin"和"admin",则利用`router.replaceUrl`方法重定向用户到指定页面。否则,通过`promptAction.showToast`表现错误消息。
  • build 方法界说了组件的结构,包括一个包含Logo、文本输入框、按钮等的界面。
页面心得:


  • 清晰的结构:
    代码结构清晰,利用了ArkTs提供的组件和装饰器,使得组件的声明和状态管理更加简洁。
  • 用户界面(UI):
    界面设计简单直观,包括Logo、用户名、密码输入框以及登录按钮。
    利用了Ark Ts的UI组件,使得构建用户界面的过程更加方便。
  • 登录逻辑:
    登录逻辑通过`S_login`方法实现,检查用户名和密码是否匹配,并利用`router.replaceUrl`进行页面重定向。
    错误处理利用了`promptAction.showToast`提供用户友好的错误提示。
  • 状态管理(State):
    利用`@State`来管理组件的状态,这样可以在状态变革时触发UI的更新。
3.3.2 运动列表页面


核心代码
  1. // 搜索框
  2. Search({
  3.   value: this.filterText, // 将搜索框的值与 filterText 关联
  4.   placeholder: '输入搜索关键字...',
  5.   // controller: this.controller
  6. })
  7.   .onChange((value: string) => {
  8.     this.filterText = value; // 设置搜索框的值到 filterText
  9.   })
  10.   .width('90%')
  11. // .margin(20);
  12.   Scroll(this.scroller)
  13.    Column()
  14.      // 列出活动信息
  15.      ForEach(this.activities, (activity, index) => {
  16.        // 根据 filterText 过滤活动
  17.        if (
  18.          this.filterText === '' || // 如果搜索框为空,显示所有活动
  19.          activity.title.includes(this.filterText) || // 活动标题包含搜索关键字
  20.          activity.type.includes(this.filterText) // 活动类型包含搜索关键字
  21.        ) {
  22.          // 如果满足搜索条件,显示活动
  23.          Row() {
  24.            Column() {
  25.              Row() {
  26.                Text(`${index + 1}.)
  27.                  .width("10%")
  28.                  .fontSize("20fp");
  29.                Text(activity.title) // 活动标题
  30.                  // .width("30%")
  31.                  .fontSize("20fp")
  32.              }
  33.              Image('images/'+String(activity.type)+".png")
  34.                .margin({left:40,top:10})
  35.                .width("80%")
  36.                .height("300px")
  37.                .onClick(() => {
  38.                  const secondaryButton = {
  39.                    value: '我要报名',
  40.                    fontColor: '#ffffff', // 可选,文字颜色
  41.                    backgroundColor: '#007aff', // 可选,背景颜色
  42.                    action: () => {
  43.                      // @ts-ignore
  44.                      this.activities[index].flag='1'
  45.                      // @ts-ignore
  46.                      console.log(this.activities[index].flag)
  47.                      AlertDialog.show({
  48.                        title: "报名成功",
  49.                        message:"您已成功报名此活动"
  50.                      });
  51.                    } // 按钮被点击后执行的函数
  52.                  };
  53.                  const primaryButton = {
  54.                    value: '取消',
  55.                    fontColor: '#ffffff', // 可选,文字颜色
  56.                    backgroundColor: 'red', // 可选,背景颜色
  57.                    action: () => {
  58.                    } // 按钮被点击后执行的函数
  59.                  };
  60.                  // 处理查看活动详情的逻辑
  61.                  AlertDialog.show({
  62.                    title: "活动详情",
  63.                    message: `标题: ${activity.title}\n时间 ${activity.time}\n地点:${activity.where}\n详细信息: ${activity.description}\n\n`
  64.                    secondaryButton: secondaryButton,
  65.                    primaryButton: primaryButton,
  66.                  });
  67.                });
  68.            }
  69.          }
  70.          .width("95%")
  71.          .margin({ top: '10vp' })
  72.          .margin(10)
  73.        }
  74.      });
复制代码
这段代码是一个带有搜刮框的运动列表页面,用户可以在搜刮框中输入关键字,然后根据关键字过滤和表现相关的运动信息。以下是代码的一些关键点:


  • 搜刮框:
    利用`Search`组件创建了一个搜刮框,其值与`this.filterText`关联。
    通过`onChange`变乱监听搜刮框值的变革,并将新的值设置给`this.filterText`
  • 运动列表:
    利用`Scroll`组件创建了一个可滚动的视图。
    利用`ForEach`遍历`this.activities`数组中的每个运动,根据搜刮框的值过滤运动信息。
  • 过滤逻辑:
    根据搜刮框的值(`this.filterText`),过滤表现符合条件的运动信息。
    如果搜刮框为空,表现所有运动;否则,只表现标题或范例包含搜刮关键字的运动。
  • 运动信息表现:
    对于每个满足条件的运动,创建一个`Row`组件,表现运动的标题、序号、图片等信息。
    图片点击变乱:点击运动的图片,弹出对话框表现运动详情,并提供报名和取消按钮。
  • 报名逻辑:
    点击图片时,弹出包含报名和取消按钮的对话框。
    如果点击了报名按钮,将运动的`flag`属性设置为’1’,并表现报名成功的对话框。
  • 对话框:
    利用`AlertDialog`组件表现运动详情和报名结果对话框。
    对话框包括标题、时间、地点、详细信息,并可包含报名和取消按钮。
3.3.3 个人中心页面


本页面个人中心是一个简单的UI页面。
核心代码:
  1. Column() {
  2.   Column() {
  3.     Stack() {
  4.       Image($r('app.media.toxiang'))
  5.         .width("131.1vp")
  6.         .height("139.21vp")
  7.         .offset({ x: "-0.33vp", y: "-20.98vp" })
  8.     }
  9.     .width("99.7%")
  10.     // .height("105.66vp")
  11.     .offset({ x: "0.46vp", y: "-292.54vp" })
  12.     .margin(20)
  13.     // .backgroundColor("#8adff5")
  14.     Stack() {
  15.       Stack() {
  16.         Row(){
  17.           Image($r('app.media.exid'))
  18.             .width("40.68vp")
  19.             .height("40.79vp")
  20.           Text("  退出登录")
  21.             .width("262.68vp")
  22.             .height("43.79vp")
  23.             .fontSize("20fp")
  24.         }
  25.       }
  26.       // .backgroundColor("#a0d9f6")
  27.       .width("90%")
  28.       .height("62.73vp")
  29.       .offset({ x: "1.33vp", y: "-36.77vp" })
  30.       .onClick(()=>{
  31.         router.replaceUrl({
  32.           url: "pages/Index"
  33.           // this.paramsFromIndex?['name']
  34.         })
  35.       })
  36.       Stack() {
  37.         Row(){
  38.           Image($r('app.media.huodong'))
  39.             .width("40.68vp")
  40.             .height("40.79vp")
  41.           Text("  新增活动")
  42.             .width("262.68vp")
  43.             .height("43.79vp")
  44.               // .offset({ x: "34.29vp", y: "-0.17vp" })
  45.             .fontSize("20fp")
  46.         }
  47.       }
  48.       // .backgroundColor("#a0d9f6")
  49.       .width("90%")
  50.       .height("62.73vp")
  51.       .offset({ x: "1.33vp", y: "-115.67vp" })
  52.       .onClick(()=>{
  53.         router.replaceUrl({
  54.           url: "pages/demo2",
  55.           params: {
  56.             activities:this.activities
  57.           }
  58.         })
  59.       })
  60.       Stack() {
  61.         Row(){
  62.           Image($r('app.media.canyu'))
  63.             .width("40.68vp")
  64.             .height("40.79vp")
  65.           Text("  已参与的活动")
  66.             .width("262.68vp")
  67.             .height("43.79vp")
  68.               // .offset({ x: "34.29vp", y: "-0.17vp" })
  69.             .fontSize("20fp")
  70.         }
  71.       }
  72.       .width("90%")
  73.       .height("62.73vp")
  74.       // .backgroundColor("#a0d9f6")
  75.       .offset({ x: "1.33vp", y: "-192.39vp" })
  76.       .onClick(() => {
  77.         router.replaceUrl({
  78.           url: "pages/canyu",
  79.           params: {
  80.             activities:this.activities
  81.           }
  82.         })
  83.       })
  84.     }
  85.     .width("99.4%")
  86.     .height("465.88vp")
  87.     .offset({ x: "0.92vp", y: "-286.87vp" })
  88.   }
  89.   .width("100%")
  90.   .height("100%")
  91.   .offset({ x: "0vp", y: "311.31vp" })
  92.   .justifyContent(FlexAlign.Center)
  93. }
  94. .width("100%")
  95. .height("100%")
复制代码
3.3.4 已到场的运动和新增运动页面



核心代码:
  1.     Column() {
  2.       Row(){
  3.         Button("返回")
  4.           .margin({left:-90})
  5.           // .width("71.45vp")
  6.           .height("47.01vp")
  7.             // .offset({ x: "-126.85vp", y: "-289.57vp" })
  8.           .onClick(() => {
  9.             router.replaceUrl({
  10.               url: "pages/one",
  11.               params: {
  12.                 activities:this.activities
  13.               }
  14.             })
  15.           });
  16.         Text("我的参与")
  17.           .margin({left:"30"})
  18.           // .width("200vp")
  19.           .height("60vp")
  20.             // .offset({ x: "73.54vp", y: "-331.74vp" })
  21.           .fontSize("24fp")
  22.           // .margin({left:"50%"})
  23. }
  24.       // 搜索框
  25.       Search({
  26.         value: this.filterText, // 将搜索框的值与 filterText 关联
  27.         placeholder: '输入搜索关键字...',
  28.         // controller: this.controller
  29.       })
  30.         .onChange((value: string) => {
  31.           this.filterText = value; // 设置搜索框的值到 filterText
  32.         })
  33.         .width('90%')
  34.       // .margin(20);
  35.       Row() {
  36.         Text("序号")
  37.           .width("20%")
  38.           .fontSize("20fp")
  39.           .fontColor(Color.Blue) // 可以调整表头的样式
  40.         .margin({left:"5%"})
  41.         Text("活动")
  42.           .width("50%")
  43.           .fontSize("20fp")
  44.           .fontColor(Color.Blue);
  45.         Text("报名情况")
  46.           .width("40%")
  47.           .fontSize("20fp")
  48.           .fontColor(Color.Blue);
  49.       }
  50.       // 列出活动信息
  51.       ForEach(this.activities, (activity, index) => {
  52.         // 根据 filterText 过滤活动
  53.         if (
  54.           this.filterText === '' || // 如果搜索框为空,显示所有活动
  55.           activity.title.includes(this.filterText) || // 活动标题包含搜索关键字
  56.           activity.type.includes(this.filterText) // 活动类型包含搜索关键字
  57.         ) {
  58.           if(activity.flag==1){
  59.             // 如果满足搜索条件,显示活动
  60.             Row() {
  61.               Column() {
  62.                 Row() {
  63.                   Text(`${index + 1}.)
  64.                     .margin({left:"5%"})
  65.                     .width("20%")
  66.                     .fontSize("20fp");
  67.                   Text(activity.title) // 活动标题
  68.                     .width("50%")
  69.                     .fontSize("20fp")
  70.                   Text("已报名") // 活动标题
  71.                     .width("40%")
  72.                     .fontSize("20fp")
  73.                 }
  74.               }
  75.             }
  76.             .width("95%")
  77.             // .height("200px")
  78.             .margin({ top: '10vp' })
  79.             .margin(10)
  80.           }
  81.         }
  82.       });
  83.     }
  84.     .width('100%')
复制代码
重要代码解释:


  • 注册了点击变乱,点击按钮时会执行一个函数,利用路由(某个路由管理器,如router)跳转到 “pages/one” 页面,并通报了一个名为activities的参数。
  • 利用ForEach遍历activities数组,对每个运动进行处理。
  • 根据filterText过滤运动,只有当搜刮框为空或者运动标题、运动范例包含搜刮关键字时,才会表现。
  • 如果运动的flag即是1,表示运动已报名,就创建一个新的行结构,包含序号、运动标题和报名情况的文本标签。
页面心得:

  • 组件化和模块化设计: 代码中利用了组件化的思想,通过创建按钮、文本、搜刮框等组件,使得界面的各个部门独立、可重用。这样的设计能够提高代码的可维护性和可读性。
  • 响应式设计:利用百分比和视口单元(vp)等,以及对搜刮框变革的监听,表明代码是为不同屏幕尺寸和设备进行了响应式设计,确保在不同情况下有较好的用户体验。
  • 条件渲染:通过条件判断,根据搜刮框的值和运动的状态,选择性地渲染页面的不同部门。这样的方式使得页面的表现更加灵活,根据具体情况呈现不同的内容。
  • 路由和导航:利用路由(某个路由管理器,如`router`)来处理页面之间的导航,通过跳转到不同的页面通报参数。这样的设计是典范的单页面应用(SPA)的做法,能够提拔用户体验。
  • 样式和排版:代码中利用了一些样式的设定,包括宽度、高度、字体巨细等,通过这些样式的调整,使得页面呈现出一致的风格和排版。
3.4元服务卡片功能详解

3.4.1 元服务卡片-所有运动列表


部门核心代码:
  1. onNextQuestion() {
  2.   const nextIndex = this.currentQuestionIndex + 1;
  3.   if (nextIndex < this.title.length) {
  4.     this.currentQuestionIndex = nextIndex;
  5.     this.showExplanation = false; // 清空显示解析状态
  6.     this.ende=false;
  7.   } else {
  8.     this.ende=true;
  9.   }
  10. }
  11. build() {
  12.   Stack() {
  13.     Image($r("app.media.img1"))
  14.       .objectFit(ImageFit.Cover)
  15.     Column() {
  16.       Text("未报名的最新活动")
  17.         .fontSize(20)
  18.         .fontColor("#0076ff")
  19.         .margin({bottom:20})
  20.       Text(`${this.currentQuestionIndex+1}`+'.'+`${this.title[this.currentQuestionIndex]}`)
  21.         .fontSize(18)
  22.         .margin({bottom:5})
  23.       Row() {
  24.         Text(`${this.time[this.currentQuestionIndex]}`)
  25.           .fontSize(15)
  26.         Text(`${this.where[this.currentQuestionIndex]}`)
  27.           .fontSize(15)
  28.           // .margin(3)
  29.           .margin({left:15})
  30.       }
  31.       // .margin({ top: '20vp' });
  32.       Image('images/'+`${this.img[this.currentQuestionIndex]}`+".png")
  33.         // .margin({left:10})
  34.         .width("100%")
  35.         .height("50%")
  36.         .onClick(() => {
  37.         });
  38.       if(this.ende){
  39.         Button('已全部报名')
  40.           .margin(1)
  41.           .fontSize(10)
  42.           .fontColor(Color.White)
  43.           .backgroundColor("#499c54")
  44.           .padding({ left: '2vp', right: '2vp' }) // 调整按钮内边距
  45.           .width("100%")
  46.           .height("10%")
  47.           .margin({ top: '4vp' })
  48.       }else {
  49.         Button('报名')
  50.           .margin(1)
  51.           .fontSize(10)
  52.           .fontColor(Color.White)
  53.           .backgroundColor("#499c54")
  54.           .padding({ left: '2vp', right: '2vp' }) // 调整按钮内边距
  55.           .width("100%")
  56.           .height("10%")
  57.           .margin({ top: '4vp' })
  58.           .onClick(() => {
  59.             this.flag[this.currentQuestionIndex]='1'
  60.             this.onNextQuestion();
  61.           });
  62.       }
  63.     }
  64.     .alignItems(HorizontalAlign.Start)
  65.     // .justifyContent(FlexAlign.End)
  66.     .padding($r('app.float.column_padding'))
  67.   }
  68. }
复制代码
下面是对重要功能的分析:

  • onNextQuestion() 函数:
    当用户点击下一题时,该函数会被调用。
    盘算下一个问题的索引(`nextIndex`),如果存在下一个问题,则更新`currentQuestionIndex`并重置表现分析状态(`showExplanation`)和竣事状态(`ende`)。
    如果不存在下一个问题(即已经是最后一题),将竣事状态(`ende`)设置为`true`
  • build() 函数:
    构建用户界面的函数,是某个 UI 组件的构建方法。
    利用`Stack`组件,包含了一个图片、文本和按钮等元素。
    图片利用了`Image`组件,表现的图片路径是由`app.media.img1`动态天生的。
    `Column`组件包含了多个文本元素,表现了当前问题的标题、时间、地点和对应的图片。
    针对不同情况(是否已全部报名),表现不同的按钮:如果已全部报名,表现一个标签按钮,表示已经全部报名。如果还有问题需要答复,表现报名按钮。点击按钮时,会更新问题的标记(`flag`)为1,并调用`onNextQuestion`函数。
  • 按钮部门:
    利用了`Button`组件构建按钮。
    按钮的文本为 “已全部报名” 或 “报名”,具体表现取决于问题是否已全部答复。
    如果是 “报名” 按钮,注册了一个点击变乱(`onClick`),点击时会将当前问题的标记(`flag`)设置为1,并调用`onNextQuestion`函数。
    设置了按钮的样式,包括字体巨细、字体颜色、背景颜色等。
  • 结构和样式:
    利用了`alignItems`和`padding`来调整结构和样式。
这段代码实现了一个动态天生问题和相应按钮的用户界面,用户可以逐一答复问题,点击按钮进行报名。同时,通过控制`currentQuestionIndex`和`ende`可以在答复完所有问题后表现不同的界面状态。用于展示一系列运动,用户逐一报名。
3.4.2 元服务卡片-未报名最新运动列表


核心代码:(部门核心代码思想和所有运动卡片相似)
  1. onNextQuestion() {
  2.   const nextIndex = this.currentQuestionIndex + 1;
  3.   if (nextIndex < this.title.length) {
  4.     this.currentQuestionIndex = nextIndex;
  5.     this.showExplanation = false; // 清空显示解析状态
  6.     this.ende=false;
  7.   } else {
  8.     this.ende=true;
  9.   }
  10. }
  11. //组会题目
  12. @State title: string[] = [];
  13. //组会地点
  14. @State where: string[] = [];
  15. //组会时间
  16. @State time: string[] = [];
  17. //组会图片
  18. @State img: string[][] = [];
  19. //是否报名'0', '0','0','0','0','0';
  20. @State selectedscore: number = 0;
  21. @State showExplanation: boolean = false; // 用于控制是否显示解析
  22. @State ende: boolean = false; // 用于控制是否结束
  23. @State currentQuestionIndex: number = 0;
复制代码
代码的分析:

  • 数据状态界说:
    `title`存储运动的标题数组。
    `where`存储运动地点的数组。
    `time`存储运动时间的数组。
    `img`存储运动图片路径的二维数组。
    `flag`存储每个运动是否报名的标志,用于判断按钮表现状态。
    `selectedscore`是用于记录用户选择的分数,但在这段代码中未被利用。
    `showExplanation`用于控制是否表现分析,但在这段代码中未被利用。
    `ende`用于控制是否竣事,影响按钮的表现状态。
    `currentQuestionIndex`记录当前运动的索引。
  • onNextQuestion() 函数:
    用于切换到下一个问题(运动)的函数。
    如果存在下一个问题,则更新当前问题的索引、清空分析状态,并将竣事状态置为 `false`
    如果不存在下一个问题(即已经是最后一个问题),将竣事状态置为 `true`
  • build() 函数:
    构建用户界面的函数,是某个 UI 组件的构建方法。
    利用了 `Stack` 组件,包含了一个图片、文本和按钮等元素。
    图片利用了 `Image`组件,表现的图片路径是由 `app.media.img1` 动态天生的。
    `Column`组件包含了多个文本元素,表现了当前运动的标题、时间、地点和对应的图片。
    针对不同情况(是否已全部报名),表现不同的按钮:如果已全部报名,表现一个标签按钮,表示已经全部报名。如果还有问题需要答复,表现报名按钮;点击按钮时,会更新问题的报名状态,并调用 `onNextQuestion` 函数。
卡片效果图如下:

四.总结

本文简要概述了HarmonyOS NEXT和元服务的根本概念,引入了ArkTS语言的特性,为读者创建了一个全局的认识。文章详细先容了DevEco Studio作为HarmonyOS开辟工具的功能和利用方法,为读者提供了一个高效、便捷的开辟情况。
在实际开辟案例方面,文章以“运动召集令元服务”的开辟为例,体系地呈现了整个开辟过程。从项目准备、创建到具体功能开辟,逐一阐述了登录页面、运动列表页面、个人中心页面等的设计和实现过程。这为读者提供了一个过细的开辟实践指南,展示了HarmonyOS元服务在解决实际问题中的强盛功能。
此外,元服务卡片功能的详细分析也为读者提供了对这一特色功能的深入相识。通过核心代码分析,读者相识了怎样构建动态天生问题和用户界面,以及怎样通过状态管理实现用户逐一报名的流程。整个文章逻辑清晰,代码解释过细,为读者提供了一份全方位的HarmonyOS元服务开辟实践手册。
五.附录



  • 元服务先容:元服务-元服务万能卡片-元服务开辟解决方案-华为开辟者联盟
  • ArkTS语言先容: ArkTS 语言 - HarmonyOS应用开辟官网
  • 端云一体化开辟先容:文档中心
  • 低代码开辟先容:文档中心

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

篮之新喜

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表