元服务,作为HarmonyOS提供的创新服务形式,具备独立入口、免安装的特性,支持多种呈现方式,如灵活的卡片式展示等。本文将深入剖析元服务的核心概念,介绍基于最新API9的开发流程,以及使用的开发工具,为读者提供全方位的元服务开发体验。
本文将为您揭示未来鸿蒙生态的精彩面貌。跟随我们一起踏上HarmonyOS的元服务开发之旅,体验鸿蒙的创新力量!
本文将基于活动召集令元服务的开发案例带大家一起学习:
元服务内部功能:
元服务卡片:
项目实现演示效果如下:
活动召集令元服务视频
元服务 (原为原子化服务) 是一种基于HarmonyOS API的全新服务提供方式,元服务仅需开发一次,便可以运行在多种类型的终端设备上,以鸿蒙万能卡片等多种呈现形态, 向用户提供更轻量化的服务。
鸿蒙万能卡片是元服务最主要的呈现形态之一(其他形态如语音、图标等),每一个万能卡片都是在桌面上“永远打开的”元服务/应用,将元服务/应用的重要信息以卡片的形式展示在桌面,通过轻量交互行为实现服务直达。
在万物互联时代,人均持有设备量不断攀升,设备和场景的多样性,使服务开发变得更加复杂、服务入口更加丰富。在此趋势下,应用提供方和用户迫切需要一种新的服务提供方式,使应用开发更简单、服务的获取和使用更便捷。为此,HarmonyOS 提供了基于元服务和应用的便捷服务。
元服务是 HarmonyOS 提供的一种面向未来的服务提供方式,是有独立入口的(用户可通过点击、碰一碰、扫一扫等方式直接触发)、免安装的(无需显式安装,由系统程序框架后台安装后即可使用)、可为用户提供一个或多个便捷服务的用户程序形态。元服务基于 HarmonyOS API 开发,支持运行在 1+8+N 设备上,供用户在合适的场景、合适的设备上便捷使用。
元服务具有随处可及、服务免安装直达、分布式流转等特性。日常生活中,用户可以通过扫描 HarmonyOS Connect 标签、“碰一碰”设备来快速启动元服务,也可以在设备的服务中心和桌面上轻松找到他。
(1) 免安装,更轻量化地将服务带给用户
(2) 一键服务直达,将用户感兴趣的内容前置、外显
(3) 跨端转移,多终端设备间无缝流转
(4) 情景智能卡片推荐,随心定制、更懂用户
ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript(简称TS)基本语法风格的基础上,对TS的动态类型特性施加更严格的约束,引入静态类型。同时,提供了声明式UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。
同时,提供了声明式UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。
面向万物互联时代,华为提出一次开发多端部署、可分可合自由流转、统一生态原生智能三大应用与服务开发理念,针对多设备、多入口、服务可分可合等特性,提供多种能力协助开发者降低开发门槛,同时HarmonyOS与OpenHarmony统一生态。HarmonyOS基于JS/TS语言体系,构建了全新的声明式开发语言ArkTS。除了兼容JS/TS语言生态,ArkTS扩展了声明式UI语法和轻量化并发机制。
DevEco Studio是面向全场景多设备,提供一站式的分布式应用开发平台,支持分布式多端开发、分布式多端调测、多端模拟仿真,全方位的质量与安全保障。
DevEco Studio是一个功能强大的移动应用开发工具,基于IntelliJ IDEA Community开源版本打造,面向华为终端全场景多设备的一站式集成开发环境(IDE),为开发者提供工程模板创建、开发、编译、调试、发布等端到端的HarmonyOS应用开发服务。
DevEco Studio的主要特性包括:
DevEco Studio的端云一体化开发是指通过集成AppGallery Connect的认证服务和云函数服务,开发者可以在创建工程时选择云开发模板,实现端云一体化协同开发。这使得开发者可以在DevEco Studio中进行认证服务的集成,以及云函数的开发和管理。
为丰富HarmonyOS对云端开发的支持、实现端云联动,DevEco Studio推出了云开发功能,开发者在创建工程时选择云开发模板,即可在DevEco Studio内同时完成HarmonyOS应用/元服务的端侧与云侧开发,体验端云一体化协同开发。
DevEco Studio的端云一体化开发包括以下方面:
相比于传统开发模式,云开发模式具备成本低、效率高、门槛低等优势,具体区别见下表。
DevEco Studio的低代码开发是指通过使用预定义的组件和模板,以及可视化的界面编辑方式,开发者可以以较少的编码工作量快速构建应用程序。低代码开发可以大大提高开发效率,降低开发门槛,使非专业的程序员也能进行应用程序的开发。
使用低代码开发应用或服务有以下两种开发方式:
在已有的HarmonyOS工程中,可以通过创建Visual文件的方式,使用低代码开发应用或服务的UI界面,要求compileSdkVersion必须为7或以上。ArkTS低代码要求compileSdkVersion必须为8或以上。
在打开的工程中,选中模块的pages文件夹,单击鼠标右键,选择New > Visual > Page。
在当今繁忙而快节奏的生活中,人们渴望有更多机会聚在一起,共度美好时光。然而,组织和参与活动往往面临着诸多挑战,例如信息不透明、难以找到合适的活动,以及活动宣传不足。为了解决这些问题,我们开发了一款全新的创意应用——活动召集令元服务。
在城市化进程加速的今天,越来越多的人发现,社交活动是缓解生活压力、建立社交网络以及增进人际关系的重要途径。然而,传统的社交方式难以满足现代人的需求。于是,我萌生了为用户提供一个便捷、高效、社交化的活动发布平台的创意。
在寻找活动信息的过程中,用户常常面临信息碎片化和不一致的情况。有些活动信息散落在不同的社交媒体或活动平台上,用户需要花费大量时间和精力来收集和筛选。我们意识到,整合和统一这些信息对提高用户体验至关重要。
随着移动互联技术的迅速发展,我们看到了整合社交元素和活动信息的机会。通过开发一款创新的应用,用户可以轻松创建、查找和参与各种社交活动。这个应用将成为人们社交生活的数字化扩展,使活动的组织和参与变得更加简单而愉快。
在这个创意应用的背后,我们追求的是连接人与人之间的纽带,让社交活动更加简单而有趣。我们相信,通过这个活动发布平台,人们可以更轻松地发现、创建和参与各种精彩活动,为生活增添更多色彩。
如下图,注册AppGallery Connect并配置相应的配置。进入我的元服务创建项目。
build() {
Row() {
Column({space:17}) {
Image($r("app.media.logo")).width(80)
Text("活动召集令")
TextInput({ placeholder: '输入用户名' })
.width(300)
.height(60)
.fontSize(20)
.onChange((value: string) => {
this.username = value
})
TextInput({ placeholder: '输入密码' })
.width(300)
.height(60)
.fontSize(20)
.type(InputType.Password)
.onChange((value: string) => {
this.password = value
})
Button('登录')
.width(300)
.height(60)
.fontSize(20)
.backgroundColor('#0F40F5')
.onClick(() => {
this.S_login();
})
}
.width('100%')
}
.height('100%')
}
@Component
是用来定义组件的装饰器,@Entry
是页面入口组件。@State
被用来定义组件内部的状态。在这里,activitiesusername`和`password`都是组件的状态。当被
@State`修饰的变量发生变化,页面会立即刷新。S_login
方法用于处理登录逻辑。如果用户名和密码是"admin"和"admin",则使用`router.replaceUrl`方法重定向用户到指定页面。否则,通过`promptAction.showToast`显示错误消息。build
方法定义了组件的结构,包括一个包含Logo、文本输入框、按钮等的界面。页面心得:
核心代码
// 搜索框
Search({
value: this.filterText, // 将搜索框的值与 filterText 关联
placeholder: '输入搜索关键字...',
// controller: this.controller
})
.onChange((value: string) => {
this.filterText = value; // 设置搜索框的值到 filterText
})
.width('90%')
// .margin(20);
Scroll(this.scroller)
Column()
// 列出活动信息
ForEach(this.activities, (activity, index) => {
// 根据 filterText 过滤活动
if (
this.filterText === '' || // 如果搜索框为空,显示所有活动
activity.title.includes(this.filterText) || // 活动标题包含搜索关键字
activity.type.includes(this.filterText) // 活动类型包含搜索关键字
) {
// 如果满足搜索条件,显示活动
Row() {
Column() {
Row() {
Text(`${index + 1}.)
.width("10%")
.fontSize("20fp");
Text(activity.title) // 活动标题
// .width("30%")
.fontSize("20fp")
}
Image('images/'+String(activity.type)+".png")
.margin({left:40,top:10})
.width("80%")
.height("300px")
.onClick(() => {
const secondaryButton = {
value: '我要报名',
fontColor: '#ffffff', // 可选,文字颜色
backgroundColor: '#007aff', // 可选,背景颜色
action: () => {
// @ts-ignore
this.activities[index].flag='1'
// @ts-ignore
console.log(this.activities[index].flag)
AlertDialog.show({
title: "报名成功",
message:"您已成功报名此活动"
});
} // 按钮被点击后执行的函数
};
const primaryButton = {
value: '取消',
fontColor: '#ffffff', // 可选,文字颜色
backgroundColor: 'red', // 可选,背景颜色
action: () => {
} // 按钮被点击后执行的函数
};
// 处理查看活动详情的逻辑
AlertDialog.show({
title: "活动详情",
message: `标题: ${activity.title}\n时间 ${activity.time}\n地点:${activity.where}\n详细信息: ${activity.description}\n\n`
secondaryButton: secondaryButton,
primaryButton: primaryButton,
});
});
}
}
.width("95%")
.margin({ top: '10vp' })
.margin(10)
}
});
这段代码是一个带有搜索框的活动列表页面,用户可以在搜索框中输入关键字,然后根据关键字过滤和显示相关的活动信息。以下是代码的一些关键点:
核心代码:
Column() {
Column() {
Stack() {
Image($r('app.media.toxiang'))
.width("131.1vp")
.height("139.21vp")
.offset({ x: "-0.33vp", y: "-20.98vp" })
}
.width("99.7%")
// .height("105.66vp")
.offset({ x: "0.46vp", y: "-292.54vp" })
.margin(20)
// .backgroundColor("#8adff5")
Stack() {
Stack() {
Row(){
Image($r('app.media.exid'))
.width("40.68vp")
.height("40.79vp")
Text(" 退出登录")
.width("262.68vp")
.height("43.79vp")
.fontSize("20fp")
}
}
// .backgroundColor("#a0d9f6")
.width("90%")
.height("62.73vp")
.offset({ x: "1.33vp", y: "-36.77vp" })
.onClick(()=>{
router.replaceUrl({
url: "pages/Index"
// this.paramsFromIndex?['name']
})
})
Stack() {
Row(){
Image($r('app.media.huodong'))
.width("40.68vp")
.height("40.79vp")
Text(" 新增活动")
.width("262.68vp")
.height("43.79vp")
// .offset({ x: "34.29vp", y: "-0.17vp" })
.fontSize("20fp")
}
}
// .backgroundColor("#a0d9f6")
.width("90%")
.height("62.73vp")
.offset({ x: "1.33vp", y: "-115.67vp" })
.onClick(()=>{
router.replaceUrl({
url: "pages/demo2",
params: {
activities:this.activities
}
})
})
Stack() {
Row(){
Image($r('app.media.canyu'))
.width("40.68vp")
.height("40.79vp")
Text(" 已参与的活动")
.width("262.68vp")
.height("43.79vp")
// .offset({ x: "34.29vp", y: "-0.17vp" })
.fontSize("20fp")
}
}
.width("90%")
.height("62.73vp")
// .backgroundColor("#a0d9f6")
.offset({ x: "1.33vp", y: "-192.39vp" })
.onClick(() => {
router.replaceUrl({
url: "pages/canyu",
params: {
activities:this.activities
}
})
})
}
.width("99.4%")
.height("465.88vp")
.offset({ x: "0.92vp", y: "-286.87vp" })
}
.width("100%")
.height("100%")
.offset({ x: "0vp", y: "311.31vp" })
.justifyContent(FlexAlign.Center)
}
.width("100%")
.height("100%")
Column() {
Row(){
Button("返回")
.margin({left:-90})
// .width("71.45vp")
.height("47.01vp")
// .offset({ x: "-126.85vp", y: "-289.57vp" })
.onClick(() => {
router.replaceUrl({
url: "pages/one",
params: {
activities:this.activities
}
})
});
Text("我的参与")
.margin({left:"30"})
// .width("200vp")
.height("60vp")
// .offset({ x: "73.54vp", y: "-331.74vp" })
.fontSize("24fp")
// .margin({left:"50%"})
}
// 搜索框
Search({
value: this.filterText, // 将搜索框的值与 filterText 关联
placeholder: '输入搜索关键字...',
// controller: this.controller
})
.onChange((value: string) => {
this.filterText = value; // 设置搜索框的值到 filterText
})
.width('90%')
// .margin(20);
Row() {
Text("序号")
.width("20%")
.fontSize("20fp")
.fontColor(Color.Blue) // 可以调整表头的样式
.margin({left:"5%"})
Text("活动")
.width("50%")
.fontSize("20fp")
.fontColor(Color.Blue);
Text("报名情况")
.width("40%")
.fontSize("20fp")
.fontColor(Color.Blue);
}
// 列出活动信息
ForEach(this.activities, (activity, index) => {
// 根据 filterText 过滤活动
if (
this.filterText === '' || // 如果搜索框为空,显示所有活动
activity.title.includes(this.filterText) || // 活动标题包含搜索关键字
activity.type.includes(this.filterText) // 活动类型包含搜索关键字
) {
if(activity.flag==1){
// 如果满足搜索条件,显示活动
Row() {
Column() {
Row() {
Text(`${index + 1}.)
.margin({left:"5%"})
.width("20%")
.fontSize("20fp");
Text(activity.title) // 活动标题
.width("50%")
.fontSize("20fp")
Text("已报名") // 活动标题
.width("40%")
.fontSize("20fp")
}
}
}
.width("95%")
// .height("200px")
.margin({ top: '10vp' })
.margin(10)
}
}
});
}
.width('100%')
主要代码解释:
页面心得:
onNextQuestion() {
const nextIndex = this.currentQuestionIndex + 1;
if (nextIndex < this.title.length) {
this.currentQuestionIndex = nextIndex;
this.showExplanation = false; // 清空显示解析状态
this.ende=false;
} else {
this.ende=true;
}
}
build() {
Stack() {
Image($r("app.media.img1"))
.objectFit(ImageFit.Cover)
Column() {
Text("未报名的最新活动")
.fontSize(20)
.fontColor("#0076ff")
.margin({bottom:20})
Text(`${this.currentQuestionIndex+1}`+'.'+`${this.title[this.currentQuestionIndex]}`)
.fontSize(18)
.margin({bottom:5})
Row() {
Text(`${this.time[this.currentQuestionIndex]}`)
.fontSize(15)
Text(`${this.where[this.currentQuestionIndex]}`)
.fontSize(15)
// .margin(3)
.margin({left:15})
}
// .margin({ top: '20vp' });
Image('images/'+`${this.img[this.currentQuestionIndex]}`+".png")
// .margin({left:10})
.width("100%")
.height("50%")
.onClick(() => {
});
if(this.ende){
Button('已全部报名')
.margin(1)
.fontSize(10)
.fontColor(Color.White)
.backgroundColor("#499c54")
.padding({ left: '2vp', right: '2vp' }) // 调整按钮内边距
.width("100%")
.height("10%")
.margin({ top: '4vp' })
}else {
Button('报名')
.margin(1)
.fontSize(10)
.fontColor(Color.White)
.backgroundColor("#499c54")
.padding({ left: '2vp', right: '2vp' }) // 调整按钮内边距
.width("100%")
.height("10%")
.margin({ top: '4vp' })
.onClick(() => {
this.flag[this.currentQuestionIndex]='1'
this.onNextQuestion();
});
}
}
.alignItems(HorizontalAlign.Start)
// .justifyContent(FlexAlign.End)
.padding($r('app.float.column_padding'))
}
}
下面是对主要功能的分析:
onNextQuestion()
函数:build()
函数:这段代码实现了一个动态生成问题和相应按钮的用户界面,用户可以逐一回答问题,点击按钮进行报名。同时,通过控制`currentQuestionIndex`和`ende`可以在回答完所有问题后显示不同的界面状态。用于展示一系列活动,用户逐一报名。
核心代码:(部分核心代码思想和所有活动卡片相似)
onNextQuestion() {
const nextIndex = this.currentQuestionIndex + 1;
if (nextIndex < this.title.length) {
this.currentQuestionIndex = nextIndex;
this.showExplanation = false; // 清空显示解析状态
this.ende=false;
} else {
this.ende=true;
}
}
//组会题目
@State title: string[] = [];
//组会地点
@State where: string[] = [];
//组会时间
@State time: string[] = [];
//组会图片
@State img: string[][] = [];
//是否报名'0', '0','0','0','0','0';
@State selectedscore: number = 0;
@State showExplanation: boolean = false; // 用于控制是否显示解析
@State ende: boolean = false; // 用于控制是否结束
@State currentQuestionIndex: number = 0;
代码的分析:
onNextQuestion()
函数:build()
函数:本文简要概述了HarmonyOS NEXT和元服务的基本概念,引入了ArkTS语言的特性,为读者建立了一个全局的认识。文章详细介绍了DevEco Studio作为HarmonyOS开发工具的功能和使用方法,为读者提供了一个高效、便捷的开发环境。
在实际开发案例方面,文章以“活动召集令元服务”的开发为例,系统地呈现了整个开发过程。从项目准备、创建到具体功能开发,逐一阐述了登录页面、活动列表页面、个人中心页面等的设计和实现过程。这为读者提供了一个详尽的开发实践指南,展示了HarmonyOS元服务在解决实际问题中的强大功能。
此外,元服务卡片功能的详细解析也为读者提供了对这一特色功能的深入了解。通过核心代码分析,读者了解了如何构建动态生成问题和用户界面,以及如何通过状态管理实现用户逐一报名的流程。整个文章逻辑清晰,代码解释详尽,为读者提供了一份全方位的HarmonyOS元服务开发实践手册。