JavaScript
做为一种轻量级
,解释型
的即时编译型的编程语言
在移动应用开发中得到了开发者的青睐,其在微信小程序开发
和uni-App开发
中得到了较多使用。不同与小程序,HarmonyOs Js框架拥有独特的代码优化模式,JS代码可以在HarmonyOs系统中快速运行。开发复杂页面布局
,掌握常用组件
、容器组件
和自定义组件
的用法,学会使用JS FA访问PA的方法
。跨设备的高性能UI开发框架
,支持声明式编程
和跨设备多态U
I。需要掌握以下基础知识:
基础能力
采用类HTML和CSS声明式编程语言
作为页面布局
和页面样式
的开发语言,页面业务逻辑
则支持ECMAScript规范的JavaScript
语言。JS UI框架提供的声明式编程,可以让开发者避免编写UI状态切换的代码,视图配置信息更加直观。支持UI跨设备显示能力
,运行时自动映射到不同设备类型,开发者无感知,降低开发者多设备适配成本
。列表
、图片
和各类容器组件
等,针对声明式语法进行了渲染流程的优化。整体架构
应用层(Application )
、前端框架层(Framework )
、引擎层(Engine )
和平台适配层(Porting Layer
) 。前端页面解析
,以及提供MVVM ( Model-View-ViewModel)开发模式
、页面路由机制
和自定义组件
等能力。引擎层&适配层
动画解析
、DOM ( Documen Object Model)树构建
、布局计算
、渲染命令构建与绘制、事件管理等能力。对平台层进行抽象,提供抽象接口,可以对接到系统平台
。比如:事件对接、渲染管线对接和系统生命周期对接等。AceAbility类是JS FA在HarmonyOS上运行环境的基类,继承自Ability。开发者的应用运行入口类
应该从该类派生。
public class MainAbility extends AceAbility {
@Override
public void onStart(Intent intent){
super.onStart(intent);
}
@Override
public void onStop() {
super.onStop();
}
}
应用生命周期
和页面生命周期
,应用通过AceAbility类中setInstanceName()接口
设置该Ability的实例资源,并通过AceAbility窗口进行显示以及全局应用生命周期管理。参数"name"指实例名称
,实例名称与config.json
文件中module.js.name的值对应。生命周期
加载主页面
public class MainAbility extends AceAbility {
@Override
public void onStart(Intent intent){
setInstanceName("ISComponentName"); // config.json配置文件中module.js.name的标签值。
super.onStart(intent);
}
限定词文件
英文模式
下页面显示的变量内容
。同理,zh-CN.json中定义了中文模式下的页面内容。{
"strings": {
"hello": "Hello",
"world": "World"
},
"files": {}
}
页面结构.hml文件
index页面的结构
、index页面中用到的组件
,以及这些组件的层级关系
。例如: index.hml文件中包含了一个text组件,内容为“Hello World”文本。<div class ="container">
<text class ="title">
{{ $t('strings.hello') }{ftitle}}
text>
div>
页面样式.css文件
页面的样式
。例如:index.css文件定义了“container”和“title”的样式
。.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.title {
font-size: 100px;
}
页面交互.js文件
index页面的业务逻辑
,比如数据绑定、事件处理等。例如:变量“title”赋值为字符串“world”
。export default {
data: {
title: ",
},
onlnit() {
this.title = this.$t('strings.world');
},
}
构建页面结构
。在进行代码开发之前,首先要对页面结构进行分析,将页面分解为不同的部分,用容器组件来承载。标题区
、展示区
和详情区
。根据此分区,可以确定根节点的子节点应按列排
列。结构分析
由两个按列排列的text组件
实现。按行排列的swiper组件和div组件
组成,如下图所示:
容器组件swiper
,包含了四个image组件构成;
通过for指令来循环创建
容器组件div
,包含了一个text组件
和四个画布组件
canvas绘制的圆形构成。
swiper组件
里展示的图片
需要放在与pages目录
同级的common目录
下
<div class="container">
<div class="title">
<text class="name"> Food text>
<text class="sub-title"> Choose What You Like text>
div>
<div class="dispaly-style">
<swiper id="swiperImage" class="swiper-style">
<image src="{i$item}}" class="image-mode" focusable="true" for="{{imageList}}">image>
swiper>
div>
<div class="container">
<div class="selection-bar-container">
<div class="selection-bar">
<image src="{{$item}}" class="option-mode" onfocus="swipeTolndex({{$idx}})" onclick="swipeTolndex([{$idx]})" for="{fimageListl}">image>
div>
<div class="description-first-paragraph">
<text class="description">{{descriptionFirstParagraph}}text>
div>
<div class="cart">
<text class="{{cartStyle}}" onclick="addCart" onfocus="getFocus" onblur="lostFocus" focusable="true">{{cartText}}text>
div>
div>
div>
div>
media query管控手机
和TV
不同页面样式,页面样式还采用了css伪类的写法
,当点击时或者焦点移动到image组件上,image组件由半透明变成不透明
。.container {
flex-direction: column;
}
/*tv */
@media screen and (device-type: tv) {
.title {
align-items:flex-start;
flex-direction: column;
padding-left: 60px;padding-right: 160px;
margin-top:15px;
}
.swiper-style {
height: 300px;
width: 350px;
indicator-color: #4682b4;
indicator-selected-color:#fOe68c;
indicator-size: 10px;
margin-left: 50px;
}
/* phone */
@media screen and(device-type: phone){
.title {
align-items:flex-start;
flex-direction: column;
padding-left: 60px;padding-right: 160px;
padding-top: 20px;
}
.option-mode {
height: 40px;
width: 40px;
margin-left: 50px;
opacity: 0.5;
border-radius: 20px;
}
.cart-text {
font-size: 20px;
text-align: center;
width: 300px;height: 50px;
background-color: #6495ed;
color: white;
}
.cart-text-focus {
font-size: 20px;
text-align: center;
width: 300px;
height: 50px;
background-color: #4169e1;
color: white;
}
.add-cart-text {
font-size: 20px;
text-align: center;
width: 300px;
height: 50px;
background-color: #ffd700;
color: white;
}
.option-mode:focus { /*伪类,聚焦时透明度变化*/
opacity: 1;
}
}
构建页面逻辑
,主要实现的是两个逻辑功能:
焦点移动到不同的缩略图
,swiper滑动到相应的图片
;焦点移动到购物车区时
,“Add To Cart”背景颜色从浅蓝变成深蓝
,点击后文字变化为“Cart + 1”,背景颜色由深蓝色变成黄色
。添加购物车不可重复操作
。export default {
data: {
cartText: 'Add To Cart',
cartStyle: 'cart-text',
isCartEmpty: true,
descriptionFirstParagraph:'This is the food page including fresh fruit, meat,snack and etc. You can pickwhatever you like and addit to your Cart. Your orderwill arrive within 48 hours. ',
imageList:['/common/food_000.JPG',
'/common/food_001.JPG',
'/common/food_002.JPG',
'/common/food_003.JPG'],
},
swipeTolndex(index){
this.$element('swiperImage').swipeTo({index: index});
},
addCart() {
if (this.isCartEmpty){
this.cartText = 'Cart + 1';
this.cartStyle = 'add-cart-text';
this.isCartEmpty = false;
}
},
getFocus() {
if (this.isCartEmpty){
this.cartStyle = 'cart-text-focus';
}
},
lostFocus(){
if (this.isCartEmpty){
this.cartStyle = 'cart-text';
}
},
}
config.json
的“deciceType”字段中添加手机
和TV的设备类型
:{
"module": {
"deviceType": ["phone","tv"],
}
}
组件介绍
组件分类
通用属性包含常规属性
和渲染属性
。
设置组件基本标识
和外观显示特征
的属性。如何将组件在屏幕中显示出来
,就是要定义它的大小
和位置关系
。,当组件达到事件触发条件时,会执行JS中对应的事件回调函数,
实现页面uI视图和页面JS逻辑层的交互。对HarmonyOS系统来说,事件主要为
手势事件和
按键事件`。
智能穿戴等具有触摸屏的设备
,智慧屏设备
。布局说明
手机和智慧屏以720px
( px指逻辑像素,非物理像素)为基准宽度
,根据实际屏幕宽度进行缩放,例如当width设为100px时,在宽度为1440物理像素的屏幕上,实际显示的宽度为200物理像素。智能穿戴的基准宽度为454px,换算逻辑同理。页面元素
标题区域
、文本区域
、图片区域
等,每个基本元素内还可以包含多个子元素
,开发者根据需求还可以添加按钮
、开关
、进度条
等组件。在构建页面布局时,需要对每个基本元素思考以下几个问题:
页面分解
页面中的元素分解之后
再对每个基本元素按顺序实现
,可以减少多层嵌套造成的视觉混乱
和逻辑混乱
,提高代码的可读性,方便对页面做后续的调整。以下图为例进行分解:定义文档结构
实现标题
和文本区域
最常用的是基础组件text
。text组件用于展示文本,可以设置不同的属性和样式,文本内容需要写在标签内容区,在页面中插入标题和文本区域的示例如下:
<div class="container">
<text class="title-text">{{headTitle}}text>
<text class="paragraph-text">{{paragraphFirst}}text>
<text class="paragraph-text">{{paragraphSecond}}text>
div>
定义文档样式
/*xxx.cSS*/
.container {
flex-direction: column;
margin-top: 20px;
margin-left: 30px;
}
.title-text {
color: #1a1a1a;
font-size: 50px;
margin-top: 40px;
margin-bottom: 20px;
}
.paragraph-text {
color: #000000;
font-size: 35px;
line-height: 60px;
}
动态内容和交互
// xxx.js
export default {
data: {
headTitle: 'Capture the Beauty in This Moment',
paragraphFirst: 'Capture the beauty of light during the transition and fusion ofice and water. At the instant of movement and stillness, softness and rigidity,force and beauty, condensing moving moments.',
paragraphSecond: 'Reflecting the purity of nature, the innovative designupgrades your visual entertainment and ergonomic comfort. Effortlessly capturewhat you see and let it speak for what you feel.',
},
}
添加图片区域
image组件
来实现,使用的方法和text组件类似。common目录下
<image class="img" src="{{middlelmage}}">image>
/*xxx.cSS*/
.img {
margin-top: 30px;
margin-bottom: 30px;
height: 385px;
}
// xxx.js
export default {
data: {
middlelmage: '/common/ice.png',
},
}
添加留言区域
用户输入留言后点击完成
,留言区域即显示留言内容;
用户点击右侧的删除按钮可删除当前留言内容
重新输入。关联click事件
实现。开发者可以使用
input组件实现输入留言的部分
,text组件实现留言显示部分
,commentText的状态标记此时显示的组件
(通过if属性控制)。包含文本“完成”和“删除”的text组件中关联click事件
,更新commentText状态和inputValue
的内容。留言区实现
<div class="container">
<text class="comment-title">Commenttext>
<div if="{{commentText}}">
<input class="comment" value="{{finputValuel}]" onchange="updateValue()">input>
<text class="comment-key" onclick="update" focusable="true">Donetext>
div>
<div if="{{commentText}}">
<text class="comment-text" focusable="true">{{inputValue}}text>
<text class="comment-key" onclick="update" focusable="true">Deletetext>
div>
div>
/*xxx.csS */
.container {
margin-top: 24px;
background-color: #ffffff;
}
.comment-title {
font-size: 40px;
color: #1a1a1a;
font-weight: bold;
margin-top: 40px;
margin-bottom: 10px;
}
.comment {
width: 550px;
height: 100px;
background-color: lightgrey;
}
.comment-key {
width: 150px;
height: 100px;
margin-left: 20px;
font-size: 32px;
color: #1a1a1a;
font-weight: bold;
}
.comment-key:focus {
color: #007dff;
}
.comment-text {
width: 550px;
height: 100px;
text-align: left;
line-height: 35px;
font-size: 30px;
color: #O00000;
border-bottom-color: #bcbcbc;
border-bottom-width: 0.5px;
}
//xxx.js
export default {
data: {
inputValue:"",
commentText: false,
},
update(){
this.commentText = !this.commentText;
},
updateValue(e){
this.inputValue = e.text;
},
}
添加容器
基本元素组装在一起
,需要使用容器组件。在页面布局中常用到三种容器组件,分别是div、list、tabs、dialog和form
。在页面结构相对简单时,可以直接用div作为容器,因为div作为单纯的布局容器,使用起来更为方便,可以支持多种子组件。使用list组件
代替div组件实现长列表布局
,从而实现更加流畅的列表滚动体验。但是,list组件仅支持list-item作为子组件
,因此使用list时需要留意list-item的注意事项。示例如下:
<list class="list">
<list-item type="listltem" for="{{textList}}">
<text class="desc-text">{{$item.value}}text>
list-item>
list>
/*xxx.cSS*/
.desc-text {
width: 683.3px;
font-size: 35.4px;
}
// xxx.js
export default {
data: {
textList: [[value: 'JS FA']],
},
}
实现效果
加入多个list-item
,同时list-item下可以包含多个其他子组件
。页面经常需要动态加载
时,推荐使用tabs组件。tabs组件支持change事件
,在页签切换后触发
。tabs组件仅支持一个tab-bar
和一个tab-content
。具体的使用示例如下:
<tabs>
<tab-bar>
<text>Hometext>
<text>Indextext>
<text>Detailtext>
tab-bar>
<tab-content>
<image src="{{homeImage}">image>
<image src="{{indexImagel}">image>
<image src="{{detailImage}}">image>
tab-content>
tabs>
// xxx.js
export default {
data: {
homelmage: '/common/home.png',
indexlmage: '/common/index.png',
detaillmage: '/common/detail.png',
},
}
容器组件
,支持用户自定义弹窗的格式和内容,该容器组件仅支持单子件。也就是说如果需要在dialog中定义复杂元素,则这能将这些负责子组件放在其他容 器如div中。
点击非dialog区域
来取消弹窗
时会触发cancel事件;<dialog id="simpledialog" class="dialog-main" oncancel="cancelDialog">
<div class="dialog-div">
<div class="inner-txt">
<text class="txt">是否切换到玫瑰介绍?text>
div>
<div class="inner-btn">
<button
type="capsule"
value="确认"
onclick="setSchedule"
class="btn-txt"
>button>
<button
type="capsule"
value="取消"
onclick="cancelSchedule"
class="btn-txt"
>button>
div>
div>
dialog>
dialog组件运行效果
change(e) {
if (e.index == 1)
this.$element('simpledialog').show()
},
cancelDialog(e) {
prompt.showToast({message: '离开'})
},
cancelSchedule(e) {
this.$element('simpledialog').close()
prompt.showToast({message: '确认取消'})
},
提交输入信息
,支持容器内input元素的内容提交 和重置。<form onsubmit="onSubmit" onreset="onReset">
<label>菊花label>
<input type="radio" name="radioGroup" value="ju">input>
<label>玫瑰label>
<input type="radio" name="radioGroup" value="rose">input>
<text>你的评价text>
<input type="text" name="user">input>
<input type="submit">提交input>
<input type="reset">重置input>
form>
export default {
onSubmit(result) {
console.log(result.value.radioGroup);
console.log(result.value.user);
},
onReset() {
console.log("reset all value");
},
};
添加交互
添加交互
通过在组件上关联事件实现
。本节将介绍如何用div、text、image组件关联 click事件,构建一个如下图所示的点赞按钮
。 一个div组件关联click事件实现
。div组件包含一个image组件和一个text组件
:
显示未点赞和点赞的效果
。click事件函数会交替更新点赞和未点赞图片的路径
。 显示点赞数
,点赞数会在click事件的函数中同步更新
点赞效果页面结构
定义在js文件中
,可以更改isPressed
的状态,从而更新显示的 image组件。*
<div>
<div class="like" onclick="likeClick">
<image class="like-img" src="{{likeImage}}" focusable="true">image>
<text class="like-num" focusable="true">{{total}}text>
div>
div>
点赞效果样式和交互
/* xxx.css */
.like {
width: 104px;
height: 54px;
border: 2px solid #bcbcbc;
justify-content: space-between;
align-items: center;
margin-left: 72px;
border-radius: 8px;
}
.like-img {
width: 33px;
height: 33px;
margin-left: 14px;
}
.like-num {
color: #bcbcbc;
font-size: 20px;
margin-right: 17px;
}
// xxx.js
export default {
data: {
likeImage: "/common/unLike.png",
isPressed: false,
total: 20,
},
likeClick() {
var temp;
if (!this.isPressed) {
temp = this.total + 1;
this.likeImage = "/common/like.png";
} else {
temp = this.total - 1;
this.likeImage = "/common/unLike.png";
}
this.total = temp;
this.isPressed = !this.isPressed;
},
};
页面路由
将这些页面串联起来,按需实现跳转
。 页面路由router
根据页面的uri
来找到目标页面,从而实现跳转。以最基础的两个页面之 间的跳转为例,具体实现步骤如下:
构建页面结构
<div class="container">
<text class="title">This is the index page.text>
<button
type="capsule"
value="Go to the second
page"
class="button"
onclick="launch"
>button>
div>
<div class="container">
<text class="title">This is the detail page.text>
<button
type="capsule"
value="Go back"
class="button"
onclick="launch"
>button>
div>
构建页面样式
index
和detail页面
的页面样式:
/* index.css */ /* detail.css */
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.title {
font-size: 50px;
margin-bottom: 50px;
}
实现跳转
button组件
的launch方法生效
,需要在页面的.js文件中实现跳转逻辑
。调用 router.push()接口
将uri指定的页面添加到路由栈中,即跳转到uri指定的页面。// index.js
import router from "@system.router";
export default {
launch() {
router.push({
uri: "pages/detail/detail",
});
},
};
// detail.js
import router from "@system.router";
export default {
launch() {
router.back();
},
};
焦点逻辑
智慧屏的主要交互方式
,焦点逻辑的主要规则是:
焦点分发逻辑
:容器组件在第一次获焦时焦点一般都落在第一个可获焦
的子组件上, 再次获焦时焦点落在上一次失去焦点
时获焦的子组件上。容器组件一般都有特定的焦点分发逻 辑,以下分别说明常用容器组件的焦点分发逻辑。 按键移动获焦
时,焦点会移动到在移动方向上与当前获焦组件布局中心距离最近
的可获焦叶子节点上。如图中焦点在上方的横向div的第二个子组件上,当点击down按键时, 焦点要移动到下方的横向div中。这时下方的横向div中的子组件会与当前焦点所在的子组件进 行布局中心距离的计算,其中距离最近的子组件获焦
。各种组件的焦点获得
list-item
与list-item-group
,list组件每次获焦时会使第一个可获焦的item获焦
。list-item-group为特殊的list-item,且两者都与div的焦点逻辑相同
。 由自顶而下
的第一个可获焦的子组件获焦。 与div的相同
。 tab-bar
与tab-content
,tab-bar中的子组件默认都能获焦,与是否有可获焦的叶子结点无关。tab-bar与tab-content的每个页面都与div的焦点逻辑相同
。 button可获焦
,若有多个button,默认初始焦点落在第二个button上
。 popup无法获焦
focusable属性使用
通用属性focusable
主要用于控制组件能否获焦
,本身不支持焦点的组件在设置此属性后 可以拥有获取焦点的能力。如text组件本身不能获焦,焦点无法移动到它上面,设置text 的focusable属性为true后,text组件便可以获焦。特别的是,如果在没有使用focusable 属性的情况下,使用了focus,blur或key事件,会默认添加focusable属性为true。 依赖于是否拥有可获焦的子组件
。如果容器组件内没有可以获焦的 子组件,即使设置了focusable为true,依然不能获焦。当容器组件focusable属性设置为 false,则它本身和它所包含的所有组件都不可获焦。transform样式
,主要可以实现以下三种变换类型
,一次样式设置只能实现一种类型变换。
沿水平或垂直方向
将指定组件移动
所需距离; 横向或纵向
将指定组件缩小或放大到所需比例
; 旋转指定的角度
。
<div class="container">
<text class="translate">hellotext>
<text class="rotate">hellotext>
<text class="scale">hellotext>
div>
/* xxx.css */
.container {
flex-direction: column;
align-items: center;
}
.translate {
height: 150px;
width: 300px;
font-size: 50px;
background-color: #008000;
transform: translate(200px);
}
.rotate {
height: 150px;
width: 300px;
font-size: 50px;
background-color: #008000;
transform-origin: 200px 100px;
transform: rotateX(45deg);
}
.scale {
height: 150px;
width: 300px;
font-size: 50px;
background-color: #008000;
transform: scaleX(1.5);
}
静态动画
只有开始状态
和结束状态
,没有中间状态,如果需要设置中间的过渡状态
和转换效果
,需要使用连续动画
实现。 animation样式
,它定义了动画的开始状态
、结束状态
以及时间和速度的变化曲线
。通过animation样式可以实现的效果有:
背景颜色
、透明度
、宽高
和变换类型
; 元素延迟和持续的时间
; 速度曲线,使动画更加平滑
; 动画播放的次数
; 结束后是否恢复初始状态
。连续动画页面结构
css文件中先定义keyframe
,在keyframe中设置动画的过渡效果
, 并通过一个样式类型在hml文件中调用。animation-name的使用示例如下:
<div class="item-container">
<text class="header">animation-nametext>
<div class="item {{colorParam}}">
<text class="txt">colortext>
div>
<div class="item {{opacityParam}}">
<text class="txt">opacitytext>
div>
<input
class="button"
type="button"
name=""
value="show"
onclick="showAnimation"
/>
div>
连续动画页面样式
/* xxx.css */
.item-container {
margin-right: 60px;
margin-left: 60px;
flex-direction: column;
}
.header {
margin-bottom: 20px;
}
.item {
background-color: #f76160;
}
.txt {
text-align: center;
width: 200px;
height: 100px;
}
.button {
width: 200px;
font-size: 30px;
background-color: #09ba07;
}
.color {
animation-name: Color;
animation-duration: 8000ms;
}
.opacity {
animation-name: Opacity;
animation-duration: 8000ms;
}
@keyframes Color {
from {
background-color: #f76160;
}
to {
background-color: #09ba07;
}
}
@keyframes Opacity {
from {
opacity: 0.9;
}
to {
opacity: 0.1;
}
}
连续动画交互逻辑
// xxx.js
export default {
data: {
colorParam: "",
opacityParam: "",
},
showAnimation: function () {
this.colorParam = "";
this.opacityParam = "";
this.colorParam = "color";
this.opacityParam = "opacity";
},
};
手势事件
和按键事件
。
智能穿戴等具有触摸屏的设备
,智慧屏设备
由单个或多个事件识别的语义动作
(例如:点击、拖动和长按)。一个完整的 手势也可能由多个事件组成,对应手势的生命周期
。JS UI框架支持的手势事件有: 触摸 点击 长按触摸事件示例
页面结构
<div class="container">
<div class="text-container" onclick="click">
<text class="text-style">{{onClick}}text>
div>
<div class="text-container" ontouchstart="touchStart">
<text class="text-style">{{touchstart}}text>
div>
<div class="text-container" ontouchmove="touchMove">
<text class="text-style">{{touchmove}}text>
div>
<div class="text-container" ontouchend="touchEnd">
<text class="text-style">{{touchend}}text>
div>
<div class="text-container" ontouchcancel="touchCancel">
<text class="text-style">{{touchcancel}}text>
div>
<div class="text-container" onlongpress="longPress">
<text class="text-style">{{onLongPress}}text>
div>
div>
样式
/* xxx.css */
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.text-container {
margin-top: 10px;
flex-direction: column;
width: 750px;
height: 50px;
background-color: #09ba07;
}
.text-style {
width: 100%;
line-height: 50px;
text-align: center;
font-size: 24px;
color: #ffffff;
}
交互逻辑
// xxx.js
export default {
data: {
touchstart: "touchstart",
touchmove: "touchmove",
touchend: "touchend",
touchcancel: "touchcancel",
onClick: "onclick",
onLongPress: "onlongpress",
},
touchCancel: function (event) {
this.touchcancel = "canceled";
},
touchEnd: function (event) {
this.touchend = "ended";
},
touchMove: function (event) {
this.touchmove = "moved";
},
touchStart: function (event) {
this.touchstart = "touched";
},
longPress: function () {
this.onLongPress = "longpressed";
},
click: function () {
this.onClick = "clicked";
},
};
智慧屏上特有的手势事件
,当用户操作遥控器按键时触发
。
一般为用户按下按键且不松开
, 此时repeatCount将返回次数。每个物理按键对应各自的按键值(keycode)以实现不同 的功能按键事件示例
按键示例页面结构和样式
<div class="card-box">
<div class="content-box">
<text
class="content-text"
onkey="keyUp"
onfocus="focusUp"
onblur="blurUp"
>{{up}}text
>
div>
<div class="content-box">
<text
class="content-text"
onkey="keyDown"
onfocus="focusDown"
onblur="blurDown"
>{{down}}text
>
div>
div>
/* xxx.css */
.card-box {
flex-direction: column;
justify-content: center;
}
.content-box {
align-items: center;
height: 200px;
flex-direction: column;
margin-left: 200px;
margin-right: 200px;
}
.content-text {
font-size: 40px;
text-align: center;
}
按键事件处理
获焦事件向下分发
,因此示例中使用了focus事件
和blur事件
明确当前焦点 的位置。点按上下键选中up或down按键
,即相应的focused状态,失去焦点的按键恢复 正常的up或down按键文本。按确认键后该按键变为keyed状态
。// xxx.js
export default {
data: {
up: 'up',
down: 'down',
},
focusUp: function() {
this.up = 'up focused';
},
blurUp: function() {
this.up = 'up';
},
keyUp: function() {
this.up = 'up keyed';
},
focusDown: function() {
this.down = 'down focused';
},
blurDown: function() {
this.down = 'down';
},
keyDown: function() {
this.down = 'down keyed';
},
}
多模输入使HarmonyOS的UI控件能够响应多种输入事件
,事件来源于用户的按键、点击、 触屏、语音等。提供创建事件能力和获取输入设备信息能力。 多模事件基类
(MultimodalEvent),派生出操作事件
(ManipulationEvent)、按键事件类
(KeyEvent)、语音事件
(SpeechEvent)等,另 外提供创建事件类
和获取输入设备信息类
创建KeyEventListener
; onKeyEvent
(Component component, KeyEvent event) 方法; private Component.KeyEventListener onKeyEvent = new Component.KeyEventListener()
{
@Override
public boolean onKeyEvent(Component component, KeyEvent keyEvent) {
if (keyEvent.isKeyDown()) {}
}
}
自定义组件
,用户可根据业务需求将已有的组件进行扩展
,增加自定义的 私有属性
和事件
,封装成新的组件,方便在工程中多次调用,提高页面布局代码的可读 性。具体的封装方法示例如下子组件页面结构和样式
<div class="item">
<text class="title-style">{{title}}text>
<text class="text-style"
onclick="childClicked" focusable="true">
点击这里查看隐藏文本text>
<text class="text-style"
if="{{showObj}}">hello worldtext>
div>
/* comp.css */
.item {
width: 700px;
flex-direction: column;
height: 300px;
align-items: center;
margin-top: 100px;
}
.text-style {
width: 100%;
text-align: center;
font-weight: 500;
font-family: Courier;
font-size: 36px;
}
.title-style {
font-weight: 500;
font-family: Courier;
font-size: 50px;
color: #483d8b;
}
// comp.js
export default {
props: {
title: {
default: 'title',
},
showObject: {},
},
data() {
return {
showObj: this.showObject,
};},
childClicked () {
this.$emit('eventType1', {text: '收到子组件参数'});
this.showObj = !this.showObj;
},
}
父组件代码
<element name='comp'
src='../../common/component/comp.hml'>element>
<div class="container">
<text>父组件:{{text}}text>
<comp title="自定义组件" show-object="{{isShow}}"
@event-type1="textClicked">comp>
div>
/* xxx.css */
.container {
background-color: #f8f8ff;
flex: 1;
flex-direction: column;
align-content: center;
}
// xxx.js
export default {
data: {
text: '开始',
isShow: false,
},
textClicked (e) {
this.text = e.detail.text;
},
}
添加自定义属性
向子组件传递了名称为title的参数
,
props 中接收
,同时子组件也通过事件绑定向上传递了参数text
,接收时通过e.detail获取
,要 绑定子组件事件,父组件事件命名必须遵循事件绑定规则
。自定义组件效果如下图所示:JS FA(Feature Ability
)调用Java PA(Particle Ability
)的机制,该机 制提供了一种通道来传递方法调用
、数据返回
以及订阅事件上报
。 Ability
和Internal Ability
两种调用方式,开发者可以根据业务场景选择合适的调用方式进行开发。
远端进程通信
拉起并请求PA服务,适用于基本服务供多FA调用或者服务在后台独立运行的场景
; 内部函数调用的方式和FA进行通信
,适用于对服务响应时延要求较高的场景
。该方式下PA不支持其他FA访问调用交互流程
bundleName
和abilityName
来进行关联。在系统收到JS调用请求后, 根据开发者在JS接口中设置的参数来选择对应的处理方式。开发者在onRemoteRequest()
中实现PA提供的业务逻辑FA调用PA接口
FA端提供以下三个JS接口
:
两类接口
PA端提供以下两类接口
:
远端进程通信
拉起并请求PA服务; 内部函数调用
的方式 和FA进行通信callAbility返回报错
:"Internal ability not register."返回该错误说明JS接口调用请求未在系统中找到对应的InternalAbilityHandler进行处理
,因此需要检查以下几点是否正确执行:
执行了register方法
; bundleName和abilityName
与AceInternalAbility继承类构造函数中填写的名 称保持相同
,大小写敏感
; abilityType
(0:Ability; 1:Internal Ability),确保没有将abilityType 缺省或误填写为Ability方式Ability和Internal Ability的区别
同步参数
返回的结果都是Promise对象
,因此无论该参数取何值,都采用异步方式等待PA 侧响应
;通过 同步的方式获取结果后返回
;或者异步执行PA逻辑,获取结果后使用 remoteObject.sendRequest的方式将结果返回FA。 JS端调用FeatureAbility接口,传入两个Number参数,Java端接收后返回两个数的和。
var actionData = {};
actionData.firstNum = 1234;
actionData.secondNum = 2048;
var action = {};
action.bundleName = 'com.huawei.hiaceservice';
action.abilityName = 'com.huawei.hiaceservice.ComputeServiceAbility';
action.messageCode = ACTION_MESSAGE_CODE_PLUS;
action.data = actionData;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;
var result = await FeatureAbility.callAbility(action);
var ret = JSON.parse(result);
if (ret.code == 0) {
console.info('plus result is:' + JSON.stringify(ret.abilityResult));
}
public class ComputeServiceAbility extends Ability {
private static final String TAG = "ComputeServiceAbility";
private MyRemote remote = new MyRemote();
// FA在请求PA服务时会调用AbilityconnectAbility连接PA,连接成功后,需要在onConnect返回一个remote对象,
供FA向PA发送消息
@Override
protected IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
return remote.asObject();
}
class MyRemote extends RemoteObject implements IRemoteBroker {
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
@Override
public IRemoteObject asObject() {
return this;
}
}
}
}
Number参数,Java端接收后返回两个数的和。
var actionData = {};
actionData.firstNum = 1234;
actionData.secondNum = 2048;
var action = {};
action.bundleName = 'com.huawei.hiaceservice';
action.abilityName = 'com.huawei.hiaceservice.ComputeServiceAbility';
action.messageCode = ACTION_MESSAGE_CODE_PLUS;
action.data = actionData;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;
var result = await FeatureAbility.callAbility(action);
var ret = JSON.parse(result);
if (ret.code == 0) {
console.info('plus result is:' + JSON.stringify(ret.abilityResult));
}
[外链图片转存中…(img-xfpZfeWQ-1642927376083)]
public class ComputeServiceAbility extends Ability {
private static final String TAG = "ComputeServiceAbility";
private MyRemote remote = new MyRemote();
// FA在请求PA服务时会调用AbilityconnectAbility连接PA,连接成功后,需要在onConnect返回一个remote对象,
供FA向PA发送消息
@Override
protected IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
return remote.asObject();
}
class MyRemote extends RemoteObject implements IRemoteBroker {
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
@Override
public IRemoteObject asObject() {
return this;
}
}
}
}