在这里插入图片描述

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀

前言

HarmonyOS 7 对应 API 26.0.0。华为在这一版本的 ArkUI 中加入了智慧手势相关能力:系统可以结合用户的手势操作意图推断目标组件,并执行选中、点击、滚动、翻页、返回等交互;应用也可以通过 SmartGestureController 接收系统给出的手势处理建议,再决定是否介入。

这类能力和我们平时写的 onClick、TapGesture 不太一样。接入时真正需要理清的不是“再注册一种点击事件”,而是三个层次:页面先启用智慧手势,组件声明自己是否参与智慧手势响应,应用再通过监听器读取系统已经推断出的 action 和操作意图。

这次用一个最小页面把这条链路跑通:一个可被智慧手势选中的文本组件,同时保留普通 onClick,再通过监听器观察 action、operateIntention 和目标节点。

一、智慧手势和普通触摸手势不是一回事

普通触屏交互的起点是触摸屏输入。用户的手指接触屏幕后,应用可以处理触摸事件,也可以使用 ArkUI 的 TapGesture、PanGesture 等手势系统完成点击、滑动之类的识别。华为的交互文档同样建议,对于常规交互优先使用手势系统,而不是直接处理底层输入事件。

智慧手势的关注点更靠上一层。

根据 HarmonyOS 7 官方资料,系统会根据用户的手势操作意图自动推断目标组件,并进一步执行选中、点击、滚动、翻页、返回等动作。开发者接收到的并不是一串需要自己分析的触点轨迹,而是已经经过系统处理的智慧手势 Proposal。

因此可以把两者粗略理解成:

维度普通触摸/ArkUI 手势API 26 智慧手势
开发者主要处理对象点击、滑动等组件事件或 Gesture系统给出的智慧手势处理 Proposal
目标组件通常由命中测试和组件事件链决定系统可结合操作意图推断目标
业务入口onClick、Gesture 回调等默认组件行为 + registerMonitor 监听/干预
典型数据触摸或手势事件action、operateIntention、目标节点等
本文涉及版本既有 ArkUI 能力26.0.0 起

这里比较容易理解错:智慧手势不是让应用重新识别一套手指轨迹,而是让应用接入系统已经建立的智慧交互链路。

二、先把 API 26 的使用条件确认清楚

HarmonyOS 7.0 对应的 API 版本是 26.0.0,官方升级适配文档也建议使用与 26.0.0 配套的开发套件进行适配。

BaseGestureHandlingProposal 从 26.0.0 开始提供,只能在 Stage 模型下使用,所属系统能力为 SystemCapability.ArkUI.ArkUI.Full。官方 API 页面当前标注 Phone、PC/2in1、Tablet、TV、Wearable 的 26.0.0+ API 范围。

本文会用到的核心能力可以归到下面几项:

this.getUIContext().getSmartGestureController() 用于取得当前 UI 上下文对应的智慧手势控制器;enableSmartTapAndSlideGestures(true) 用于开启相应智慧手势;registerMonitor(...) 注册监听;BaseGestureHandlingProposal 提供 action 和 operateIntention;带目标节点的 Proposal 还可以通过 TargetedGestureProposal.node 获得对应的 FrameNode。官方示例在页面消失时调用 clearMonitors(),并关闭此前启用的智慧手势。

组件侧还有一个很关键的通用属性:

.smartGestureShortcut({
  action: GestureShortcut.PRIMARY,
  enabled: true,
  selectable: true
})

smartGestureShortcut 同样从 26.0.0 开始支持,而且仅限 Stage 模型。这里的 enabled 决定组件是否响应智慧手势,selectable 控制被智慧手势选中后是否展示并保留选中态;当前 action 支持的优先级配置为 GestureShortcut.PRIMARY。特别要注意,官方明确说明:这个属性只负责声明组件的智慧手势响应行为,本身不会直接触发点击、滚动、翻页或返回。

在本文核对的上述智慧手势 API 参考中,没有额外声明需要申请的应用权限,因此这里不应为了“接手势”自行向 module.json5 添加一个猜测出来的权限。真正的前置条件是 API 版本、Stage 模型和 ArkUI 能力。

三、搭一个最小实践:智慧手势和普通点击共存

场景保持简单:页面上放一个文本卡片,它本来就支持普通点击;API 26 环境下再把它声明为智慧手势响应目标,同时注册全局监听,观察系统产生的智慧手势 Proposal。

这样做有一个好处:业务逻辑不需要为了智慧手势重新复制一套。

例如“打开详情”本来就在 onClick 中处理,那么系统最终执行点击动作时,仍然可以落到组件自己的点击业务上;普通触摸点击也继续使用同一个入口。官方 BaseGestureHandlingProposal 示例本身就是在配置 smartGestureShortcut 的同时保留 .onClick()。

下面代码按照官方 API 示例重新整理成一个最小页面。这里没有声称代码已经在具体机型上编译运行,实际发布前仍应使用 API 26 工具链和目标真机验证。

import {
  BaseGestureHandlingProposal,
  GestureHandlingResolution,
  TargetedGestureProposal
} from '@kit.ArkUI';

@Entry
@Component
struct SmartGestureDemo {
  private controller = this.getUIContext().getSmartGestureController();

  private smartGestureMonitor = (proposal: BaseGestureHandlingProposal) => {
    console.info(
      `smartGesture action=${proposal.action}, ` +
      `operateIntention=${proposal.operateIntention}`
    );

    const targetProposal = proposal as TargetedGestureProposal;
    console.info(`target node id=${targetProposal.node.getId()}`);

    return new GestureHandlingResolution(true);
  };

  aboutToAppear(): void {
    this.controller.enableSmartTapAndSlideGestures(true);
    this.controller.registerMonitor(this.smartGestureMonitor);
  }

  aboutToDisappear(): void {
    this.controller.clearMonitors();
    this.controller.enableSmartTapAndSlideGestures(false);
  }

  build() {
    Column({ space: 16 }) {
      Text('智慧手势目标')
        .id('smart_gesture_target')
        .fontSize(20)
        .width('100%')
        .padding(16)
        .borderWidth(1)
        .borderRadius(12)
        .smartGestureShortcut({
          action: GestureShortcut.PRIMARY,
          enabled: true,
          selectable: true
        })
        .onClick(() => {
          console.info('open detail');
          // 这里接原有的点击业务,例如打开详情页
        })

      Text('请观察控制台中的 action、operateIntention 和 nodeId')
        .fontSize(14)
    }
    .width('100%')
    .height('100%')
    .padding(16)
  }
}

真正需要关注的是 controller、smartGestureMonitor 和 smartGestureShortcut 三个位置,而不是页面布局。

四、action 和 operateIntention 分别看什么

BaseGestureHandlingProposal 有两个核心属性:action: SmartGestureAction 和 operateIntention: OperateIntention。

官方给出的定义很直接:action 表示智慧手势最终执行动作,operateIntention 表示智慧手势底层操作意图。

两者不要混成一个概念。

对业务层来说,通常应该更关注最终的 action:系统已经完成了一部分意图到动作的转换,应用可以据此了解这次智慧交互最终准备如何处理。operateIntention 更适合帮助理解这次动作背后的操作意图,或者用于调试智慧手势决策过程。

这也是为什么接入初期不建议马上写一大段业务分支。先把:

console.info(
  `action=${proposal.action}, operateIntention=${proposal.operateIntention}`
);

接起来,在目标设备上观察实际回调,再决定哪些系统动作确实需要应用自定义干预。

官方 API 还定义了 ClickActionProposal、SelectActionProposal、NoneActionProposal、BackPressActionProposal、PageSwitchActionProposal、ScrollActionProposal 等具体 Proposal 类型。由于本文的目标是先建立一条可复现的监听链路,这里不把没有逐项展开核验的枚举值硬写成 switch 分支。这样比猜一个 SmartGestureAction.XXX 更可靠。官方资料确认的是:registerMonitor 的回调参数以 BaseGestureHandlingProposal 为基类,实际会对应具体子类实例。

如果业务只是希望“智慧手势点击和手指点击都打开详情”,甚至不必把业务塞进 monitor,继续维护组件原有的:

.onClick(() => {
  console.info('open detail');
  // 打开详情
})

会更清晰。

五、什么时候需要 TargetedGestureProposal

只知道 action 还不够的时候,就需要继续看“系统认为目标是谁”。

TargetedGestureProposal 是带目标节点的智慧手势处理基类,增加了:

node: FrameNode

官方示例直接通过:

const targetProposal = proposal as TargetedGestureProposal;
console.info(`nodeId=${targetProposal.node.getId()}`);

取得目标节点 ID。

这个信息在真实页面里很有价值。一个页面可能同时存在标题、正文、操作按钮、列表和滚动区域,如果只打印 action,很难确认系统最终把这次智慧手势落到了哪个组件。给关键组件设置稳定的 .id(),再观察 Proposal 中的目标节点,就能把“动作是什么”和“动作作用于谁”对应起来。

不过这里也要克制类型转换。BaseGestureHandlingProposal 是所有智慧手势处理 Proposal 的基类,并不意味着所有 Proposal 都应该无条件当成 TargetedGestureProposal 使用。本文的最小代码主要用于观察官方示例展示的目标节点链路;业务代码进一步处理不同 Proposal 时,应继续按照相应 API 类型定义做区分,而不是靠强制类型转换猜字段。

六、为什么还要写 smartGestureShortcut

只注册 monitor 并不等于页面里的所有组件都自动变成理想的智慧手势目标。

smartGestureShortcut 是组件侧的声明入口。官方文档给出的三个配置项分别是 action、enabled 和 selectable,其中 enabled 默认值为 false;也就是说,如果业务明确希望某个组件参与智慧手势响应,最好显式配置。

例如:

.smartGestureShortcut({
  action: GestureShortcut.PRIMARY,
  enabled: true,
  selectable: true
})

表达的是“这个组件参与智慧手势响应,并作为当前支持的首选响应目标,同时允许展示选中态”。

但它不是:

smartGestureShortcut == onClick

官方已经明确说明该属性不会直接触发点击、滚动、翻页或返回动作。组件业务事件仍然应该按照组件自身的交互语义配置。

这也是智慧手势和普通点击能够自然共存的关键:一个负责告诉系统“我可以参与智慧手势”,另一个负责组件本身的点击业务。

七、低版本和不具备实际智慧手势输入条件时怎么降级

这里需要区分“API 可用范围”和“设备实际能不能产生对应智慧手势输入”。

API 参考把上述接口标为 26.0.0 起,并列出了多个设备类型,但这并不应该被理解为“任意 API 26 设备都一定拥有完全相同的智慧手势硬件和交互条件”。华为的穿戴设备资料也把智慧手势作为智能穿戴场景中的一种特色交互方式。

因此业务降级最好不要建立在“智慧手势一定会发生”这个假设上。

最稳妥的页面设计反而很简单:普通点击、滚动和导航能力照常保留,智慧手势作为额外输入方式接入。 没有产生智慧手势输入时,用户仍然可以触屏操作;有智慧手势输入时,再由系统和页面声明共同完成智慧交互。

如果应用本身还需要兼容 API 26 以下系统,则必须把 API 版本问题单独处理。华为官方升级指南明确提醒:当应用使用新版本 API,而部分设备仍运行旧版本系统时,需要进行兼容性评估和适配。官方开发者问答也给出了通过 deviceInfo.sdkApiVersion 判断系统 SDK API 版本、再隔离高版本 API 调用的做法。

例如兼容层可以先做:

import { deviceInfo } from '@kit.BasicServicesKit';

function canUseApi26(): boolean {
  return deviceInfo.sdkApiVersion >= 26;
}

但要注意:如果项目决定把 compatibleSdkVersion 本身提高到 26,那么 API 26 以下系统就不属于该应用版本的最低兼容范围,不需要再把“低版本安装运行”作为同一套页面逻辑处理。

八、生命周期别漏掉:注册和清理要成对考虑

官方智慧手势示例采用了非常清楚的生命周期结构:

aboutToAppear(): void {
  this.controller.enableSmartTapAndSlideGestures(true);
  this.controller.registerMonitor(this.smartGestureMonitor);
}

aboutToDisappear(): void {
  this.controller.clearMonitors();
  this.controller.enableSmartTapAndSlideGestures(false);
}

也就是页面出现时启用并监听,页面消失时清理监听并关闭此前启用的智慧手势。

这个结构建议直接保留。

如果只关注 registerMonitor(),却忘了页面退出后的清理,很容易把监听器生命周期和页面生命周期拆开。特别是实际工程存在多个页面时,后续排查“为什么这个页面之外还能收到相关处理”会变得麻烦。

这里先别急着做全局封装。最小实践阶段,让 controller 的生命周期跟页面保持一致,更容易确认问题到底来自系统输入、组件声明还是自己的业务代码。

九、真机验证时重点看什么

智慧手势属于交互输入能力,只看代码静态结构不够。验证时建议围绕一条完整链路进行:确认目标设备和系统版本满足当前能力条件;进入页面后确认智慧手势已启用;观察 registerMonitor 是否收到 Proposal;同时记录 action 与 operateIntention;对于带目标节点的处理再核对 node.getId() 是否符合预期;随后确认普通触摸点击仍能正常触发原有 onClick;页面退出后再检查监听生命周期是否已经结束。

不要仅凭“普通点击能用”判断智慧手势已经接入成功。普通 onClick 和智慧手势的监听链路是两个不同层面的东西。

同样,也不要只在代码中看到 .smartGestureShortcut() 就认为已经完成全部接入。官方文档明确说它只是组件响应声明,并不会自己触发最终动作。

十、实际项目可以按这个顺序排查

遇到“智慧手势没有反应”时,不建议一上来改业务回调。更有效的顺序是从能力边界往业务层收缩:先确认系统/API 是否达到 26.0.0,再确认工程使用 Stage 模型和 API 26 配套工具链;随后检查是否取得 SmartGestureController 并启用了智慧手势;确认目标组件是否显式设置了 smartGestureShortcut({ enabled: true, ... });再观察 monitor 有没有收到 Proposal,以及 action、operateIntention 和目标节点是否符合预期;这些都正常之后,才去检查最终的 onClick、滚动、导航等业务处理。

这样能把“输入没有发生”“系统没有选中目标”“监听没有接上”和“业务代码没有执行”分开,不至于全部归因到一个 onClick 上。

开发经验总结

API 26 的智慧手势接口本身并不复杂,真正需要建立的是正确的分层认识。

SmartGestureController 负责智慧手势的启用和监听;BaseGestureHandlingProposal 提供系统最终动作与底层操作意图;TargetedGestureProposal 可以继续告诉我们目标节点;smartGestureShortcut 负责声明组件是否参与智慧手势响应,而组件原有的 onClick 等业务事件没有必要因此被删除。

对于一个已经存在的 HarmonyOS 页面,更适合的接入方式不是“把原来的点击全部改成智慧手势”,而是保留原有触摸交互,把智慧手势增加为新的输入通道。这样即使目标设备没有产生对应智慧手势输入,页面本身仍然保持完整的基础交互能力。

还有一点很重要:本文没有为了写出一个漂亮的 switch 而猜测 SmartGestureAction 的具体枚举成员。官方资料已经确认 Proposal 子类和 action、operateIntention 的结构,但涉及具体 action 分支时,应继续以当前 API 26 SDK 中对应枚举和 Proposal 文档为准。对于 HarmonyOS 7 这种新能力,少写一个未经核实的枚举值,比留下一段看起来完整、实际无法编译的代码更有价值。

❤️ 如果本文帮到了你…

  • 请点个赞,让我知道你还在坚持阅读技术长文!
  • 请收藏本文,因为你以后一定还会用上!
  • 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐