HarmonyOS 鸿蒙Next特性在RN侧的适配

HarmonyOS 鸿蒙Next特性在RN侧的适配

1. 引言

1.1 欢迎使用

在 React Native 工程中使用 HarmonyOS 特性功能,包括 意图分享、一镜到底 和 碰一碰传输。这些特性可显著提升用户交互体验,实现更加便捷和自然的跨设备数据传输方式。

为了帮助您快速上手,我们提供了以下资源供参考:

1.2 文档目的

本文档旨在向开发者清晰地传达以下关键信息:

  1. 理解一镜到底、意图框架和碰一碰分享。
  2. 如何在RN工程中集成意图框架、碰一碰和一镜到底的功能。
  3. 建议在使用开发前,仔细阅读本文档。

2. 效果演示

图1 意图分享成功

意图分享成功

图2 小艺推荐中出现分享卡片

小艺推荐中出现分享卡片

图3 未开启碰一碰监听

未开启碰一碰监听

图4 碰一碰分享待接收

碰一碰分享待接收

图5 一镜到底过度效果

一镜到底过度效果

3. 意图框架介绍

3.1 Intents Kit简介

Intents Kit(意图框架服务)是HarmonyOS级的意图标准体系 ,意图连接了应用/元服务内的业务功能。

意图框架能帮开发者将应用/元服务内的业务功能,智能分发到各系统入口,这个过程即智慧分发。其中系统入口包括:小艺对话、小艺搜索、小艺建议。

3.2 Intents Kit优势

利用HarmonyOS的大模型、多维设备感知等AI能力,准确且及时地获取到用户显性、潜在意图,从而实现个性化、多模态、精准的智慧分发。

3.3 意图的运行逻辑

HarmonyOS、应用/元服务的交互中,意图运行方式分为意图调用和意图共享:

意图运行逻辑

3.4 约束与限制

设备限制

本Kit仅适用于Phone、Tablet、PC/2in1,暂不支持模拟器使用。

地区限制

本Kit仅支持中国境内(不包含中国香港、中国澳门、中国台湾)提供服务。

操作系统限制

HarmonyOS 5.0及以上。

4. 手机碰一碰分享介绍

Share Kit推出碰一碰分享,支持用户通过碰一碰发起跨端分享,可实现传输图片、共享Wi-Fi等。

(1) 场景介绍

宿主应用可以分享多个内容,如选中的多张图片等。

(2) 业务流程

图6

业务流程

流程说明:

  1. 宿主应用注册碰一碰分享事件,并与亮屏的对端设备碰一碰。
  2. 宿主应用发现设备,调用碰一碰分享事件回调,在回调事件中构造分享数据并发送。
  3. 目标设备接收并处理分享数据。
  4. 宿主应用解除注册靠近分享事件。

(3) 使用约束

手机应用发起碰一碰分享时,双端设备需要在亮屏、解锁的状态下并且都已开启华为分享服务(系统默认开启),设备顶部轻碰即可触发。如果用户已手动关闭华为分享服务开关,轻碰事件触发时,用户会接收到系统通知提示开启。

Share Kit的处理机制:

  • 任意一端设备不支持碰一碰能力时,轻碰无任何响应。
  • 宿主应用无法获得分享结果,Share Kit会通过系统通知消息告知用户对端接收或拒绝。

(4) 环境要求

  • 支持的手机系统:HarmonyOS NEXT Release及以上版本。
  • 集成开发环境:DevEco Studio NEXT Beta1及以上版本。

5. 一镜到底介绍

(1) 概述

一镜到底动效是页面切换时对相同或者相似的两个元素做的一种位置、大小等属性匹配的过渡动画效果,有助于提升用户操作任务的效率,增强视觉的流畅感,同时也增强动效的品质感,是转场设计中重点推荐的技法。

(2) 实现原理

一镜到底动效中整个页面会以一种平滑的方式从一个场景过渡到另一个场景。这种转场效果常用于展示不同页面之间的关联性,能够给用户带来流畅的视觉体验。

根据场景,可以将一镜到底动效分为两类:

  • 共享元素 共享元素一般是转场前后持续存在的界面元素,即上文提到的持续元素,是在转场发生后希望用户关注到的焦点元素,它增强了转场的连续感。

6. 代码实现

(1) 项目需要依赖package:

{
  "@hadss/react_native_intents": "./packages/react_native_intents/hadss-react_native_intents-1.0.0-rc.0.tgz",
  "@hadss/react_native_knock_share": "./packages/react_native_knock_share/hadss-react_native_knock_share-1.0.0-rc.0.tgz",
  "@hadss/react_native_geometry_transition": "1.0.8"
}

6.1 意图框架接入

6.1.1 RN工程代码

import { Intents } from '@hadss/react_native_intents';

const handleShareIntentPlayMusic = async (data: any) => {
  await Intents.shareIntent([
    {
      'intentName': 'PlayMusic',
      'intentVersion': '1.0',
      'identifier': '// uuid, ',
      'intentActionInfo': {
        'actionMode': 'EXECUTED',
        'executedTimeSlots': {
          'executedStartTime': 1637393212000,
          'executedEndTime': 1637393112000
        },
        'currentPercentage': 50
      },
      'intentEntityInfo': {
        'entityName': 'Music',
        'entityId': 'C10194368',
        'entityGroupId': 'C10194321312',
        'displayName': '测试1',
        'description': 'NA',
        'logoURL': 'https://www-file.huawei.com/-/media/corporate/images/home/logo/huawei_logo.png',
        'keywords': ['华为音乐', '化妆'],
        'rankingHint': 99,
        'expirationTime': 1637393212000,
        'metadataModificationTime': 1637393212000,
        'activityType': ['1', '2', '3'],
        'artist': ['测试歌手1', '测试歌手2'],
        'lyricist': ['测试词作者1', '测试词作者2'],
        'composer': ['测试曲作者1', '测试曲作者2'],
        'albumName': '测试专辑',
        'duration': 244000,
        'playCount': 100000,
        'musicalGenre': ['流行', '华语', '金曲', '00后'],
        'isPublicData': false
      }
    }
  ]).then(() => {
    Alert.alert(`意图分享成功`);
  });
};

<TouchableOpacity 
  style={styles.button} 
  onPress={() => {
    setActiveIndex(8)
    handleShareIntentPlayMusic()
  }}
>
  <Text style={styles.btnText}>sharelntent</Text>
</TouchableOpacity>

6.1.2 HarmonyOS工程代码

步骤一:在工程如下目录(src/main/ets/entryability)下加入InsightIntentExecutorImpl.ets文件。

import Logger from '../utils/Logger';

import { insightIntent, InsightIntentExecutor } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { type BusinessError } from '@kit.BasicServicesKit';

const TAG: string = 'InsightIntentExecutorImpl';

/**
 * 意图执行样例
 */
export default class InsightIntentExecutorImpl extends InsightIntentExecutor {
  private static readonly PLAY_MUSIC = 'PlayMusic';
  private static readonly VIEW_REPAYMENT = 'PlayVideo';
  private static readonly ERROR_CODE = -1;

  /**
   * override 执行前台UiAbility意图
   *
   * @param name 意图名称
   * @param param 意图参数
   * @param pageLoader 窗口
   * @returns 意图调用结果
   */
  onExecuteInUIAbilityForegroundMode(name: string, param: Record<string, Object>, pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> {
    Logger.info(TAG, `onExecuteInUIAbilityForegroundMode name: ${name}, param: ${JSON.stringify(param)}`);
    // 根据意图名称分发处理逻辑
    switch (name) {
      case InsightIntentExecutorImpl.PLAY_MUSIC:
        Logger.info(TAG, `PLAY_MUSIC}`);
        return this.playMusic(param, pageLoader);
      case InsightIntentExecutorImpl.VIEW_REPAYMENT:
        Logger.info(TAG, `PLAY_Video}`);
        return this.viewRepayment(param, pageLoader);
      default:
        Logger.info(TAG, `no intent match`);
        break;
    }
    Logger.info(TAG, `no intent match`);
    return Promise.resolve({
      code: InsightIntentExecutorImpl.ERROR_CODE,
      result: {
        message: 'unknown intent'
      }
    } as insightIntent.ExecuteResult);
  }

  /**
   * 实现调用播放音乐功能
   *
   * @param param 意图参数
   * @param pageLoader 窗口
   */
  private playMusic(param: Record<string, Object>, pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> {
    return new Promise(() => {
      let para: Record<string, string> = {
        'result': `intent execute success, entityId: ${param.entityId}`
      };
      let localStorage: LocalStorage = new LocalStorage(para);
      // TODO 实现意图调用
      pageLoader.loadContent('pages/Index', localStorage)
        .then(() => {
          // TODO 调用成功的情况
          Logger.info(TAG, "Intent execute succeed");
        })
        .catch((err: BusinessError) => {
          // TODO 调用失败的情况
          Logger.error(TAG, `Intent execute failed: ${JSON.stringify(err)}`);
        });
    });
  }

  /**
   * 实现调用查看还款功能
   *
   * @param param 意图参数
   * @param pageLoader 窗口
   */
  private viewRepayment(param: Record<string, Object>, pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> {
    return new Promise(() => {
      let para: Record<string, string> = {
        'result': JSON.stringify(param)
      };
      let localStorage: LocalStorage = new LocalStorage(para);
      // TODO 实现意图调用
      pageLoader.loadContent('pages/Index', localStorage)
        .then(() => {
          // TODO 调用成功的情况
          Logger.info(TAG, "Intent execute succeed");
        })
        .catch((err: BusinessError) => {
          // TODO 调用失败的情况
          Logger.error(TAG, `Intent execute failed: ${JSON.stringify(err)}`);
        });
    });
  }
};

步骤二:在工程的entry目录下的oh-package.json5中增加依赖。

{
  "license": "",
  "devDependencies": {},
  "author": "",
  "name": "entry",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {
    "@rnoh/react-native-openharmony": "0.72.70",
    "@hadss/react_native_geometry_transition": "file:../libs/geometry_transition.har",
    "@hadss/react_native_intents": "file:../libs/intents.har",
    "@hadss/react_native_knock_share": "file:../libs/knock_share.har"
  }
}
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
import { SampleTurboModulePackage } from './turbo/SampleTurboModulePackage';
import { GeometryTransitionViewPackage } from '@hadss/react_native_geometry_transition/ts';
import { IntentsModulesPackage } from '@hadss/react_native_intents';
import { KnockShareModulePackage } from '@hadss/react_native_knock_share';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SampleTurboModulePackage(ctx),
    new GeometryTransitionViewPackage(ctx),
    new IntentsModulesPackage(ctx),
    new KnockShareModulePackage(ctx)
  ];
}

6.2 手机碰一碰接入

6.2.1 RN工程代码

import { KnockShare, SharedRecord } from '@hadss/react_native_knock_share';

// 1. 启用碰一碰监听
KnockShare.addKnockShareListener()

// 2. 启用成功后,手机碰一碰
KnockShare.setKnockShareData(shareData)

// 3. 关闭碰一碰监听
KnockShare.removeKnockShareListener();

// 本示例提供三种不同分享场景

6.2.2 HarmonyOS工程代码

import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
import { SampleTurboModulePackage } from './turbo/SampleTurboModulePackage';
import { GeometryTransitionViewPackage } from '@hadss/react_native_geometry_transition/ts';
import { IntentsModulesPackage } from '@hadss/react_native_intents';
import { KnockShareModulePackage } from '@hadss/react_native_knock_share';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SampleTurboModulePackage(ctx),
    new GeometryTransitionViewPackage(ctx),
    new IntentsModulesPackage(ctx),
    new KnockShareModulePackage(ctx)
  ];
}

6.3 一镜到底接入

6.3.1 RN工程代码

// 引入HarmonyOS一镜到底RN依赖
import { GeometryTransitionView } from '@hadss/react_native_geometry_transition';
// 使用组件包装,设定viewID,此ID和HarmonyOS工程下的ID一致
<GeometryTransitionView style={[styles.buttonContainer, { marginBottom: 12 }]} geometryViewID='test' ref={nativeRef} pageType='current'>
  <TouchableOpacity style={styles.btn} onPress={() => handlePress('bundle/knockshare.harmony.bundle', 'KnockSharePage')}>
    <Text style={{ color: 'white' }}>碰一碰分享</Text>
  </TouchableOpacity>
</GeometryTransitionView>

6.3.2 HarmonyOS工程代码

// 在页面中使用,以FeaturesPage.ets为例
// 必须用animateTo方法包裹路由跳转
aboutToAppear() {
  this.startCountDown();
  emitter.on({ eventId: Constants.EVENT_ID_2 }, () => {
    animateTo({ duration: 700 }, () => {
      this.navPathStack.pop(false);
    });
  });
}

// 此处使用了倒计时跳转和背景图。主要geometryTransition方法,入参必须和RN侧设置的ID一致
  Stack() {
    Image($r('app.media.imgBg'))
      .width('100%')
      .height('100%')
      .objectFit(ImageFit.Cover)
    Row() {
      Text(`${this.countdown} 跳过`)
        .fontSize(14)
        .fontColor(Color.White)
    }
    .backgroundColor('rgba(0, 0, 0, 0.05)')
    .alignItems(VerticalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width(77)
    .height(36)
    .margin({ top: 60, right: 20 })
    .borderRadius(18)
    .onClick(() => {
      this.countdown = 0
      clearInterval(this.timer)
    })
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#40000000')
  .geometryTransition('test')
  .alignContent(Alignment.TopEnd)

更多关于HarmonyOS 鸿蒙Next特性在RN侧的适配的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

鸿蒙Next对React Native (RN) 的适配主要涉及以下几个方面:

  1. 组件适配:鸿蒙Next提供了与RN组件对应的原生组件实现,确保基础组件如View、Text等在鸿蒙平台正常渲染。

  2. 布局兼容:支持RN的Flexbox布局系统,保持与Android/iOS一致的布局效果。

  3. 样式处理:鸿蒙Next实现了RN的样式系统,包括常用样式属性和单位转换。

  4. 事件机制:处理RN手势和触摸事件到鸿蒙事件系统的映射。

  5. 性能优化:针对鸿蒙架构优化了RN的JS-Native通信机制。

  6. 模块扩展:支持通过鸿蒙Native模块扩展RN功能。

更多关于HarmonyOS 鸿蒙Next特性在RN侧的适配的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


关于HarmonyOS Next在React Native侧的适配,目前官方提供了三个核心特性的集成方案:

  1. 意图框架(Intents Kit)集成:
  • 通过@hadss/react_native_intents包实现
  • 主要使用shareIntent方法分享业务数据
  • 需要配套实现HarmonyOS侧的InsightIntentExecutorImpl.ets处理逻辑
  • 支持音乐播放、视频播放等场景的智能分发
  1. 碰一碰分享(Knock Share):
  • 通过@hadss/react_native_knock_share包实现
  • 核心方法包括addKnockShareListener/setKnockShareData等
  • 需要设备处于亮屏解锁状态
  • 支持图片、WiFi等数据的跨设备传输
  1. 一镜到底动效:
  • 通过@hadss/react_native_geometry_transition包实现
  • 使用GeometryTransitionView组件包装需要过渡的元素
  • 需要HarmonyOS侧配合使用geometryTransition方法
  • 要求RN和原生侧viewID保持一致

这些特性都需要在HarmonyOS工程中配置对应的依赖包,并在createRNPackages中注册相应的模块包。官方提供的示例代码和文档已经展示了完整的集成流程,开发者可以按照步骤进行适配。

回到顶部