ArkUI开发 - 利用C-API(NDK)开发UI控件
点击关注公众号,“技术干货”及时达!本文为稀土掘金技术社区首发签约文章
通过前文 ArkUI Engine 系列,我们明白了在 ArkTS 侧一系列基础组件,最终其实都会映射为 C++ 环境下的实现,比如 ts 侧的 Row 最终转换为 C++ 侧的 RowComponent,在 Engine 中被赋予属性并参与后续的一些列布局
创建一个Row,并设置对应的间距
void RowModelImpl::Create(const std::optionalDimension& space, AlignDeclaration* declaration, const std::string& tag)
{
std::listRefPtrComponent children;
RefPtrRowComponent rowComponent =
AceType::MakeRefPtrOHOS::Ace::RowComponent(FlexAlign::FLEX_START, FlexAlign::CENTER, children);
ViewStackProcessor::GetInstance()-ClaimElementId(rowComponent);
rowComponent-SetMainAxisSize(MainAxisSize::MIN);
rowComponent-SetCrossAxisSize(CrossAxisSize::MIN);
if (space.has_value() && space-Value() = 0.0) {
rowComponent-SetSpace(space.value());
}
if (declaration != nullptr) {
rowComponent-SetAlignDeclarationPtr(declaration);
}
ViewStackProcessor::GetInstance()-Push(rowComponent);
}
虽然前期开发者大部分的 UI 代码编写都在 ArkTS 侧,但是一些其他特殊场景下,开发者更加希望直接调用 C++ 侧的最终实现,最终在这种诉求下,鸿蒙 next 官方也提出了 C-API 的接口,通过这些接口,开发者可以直接在 C++ 侧创建出对应的 UI 产物
ArkUI 提供 C/C++ 环境接口跨平台方案中,其中有比较代表性的就是 ReactNative 了,在 ReactNative 中,开发者可以直接通过编写 js 组件代码,便可以通过映射关系转换为对应平台的原生控件实现。
在 ReactNative 中,最终原生控件生成需要进行一层映射,比如 Yoga 生成的构建树需要转换为对应原生控件的树,但是在鸿蒙中,转换流程却略微复杂。鸿蒙中对应的原生控件即 ArkTS 侧的组件,如下图:
我们发现控件的创建需要经过多层的复杂映射,这无疑是对性能有了进一步的损耗,同时 ArkTS 本身的中间产物就是 js,最终映射产物是 C++ 侧的控件实现,但是两者均无法在运行时更方便的融合入 ReactNative 方案中。C-API 方案就应运而生了:
C-API 方案 可以不仅可以运用在 ReactNative 中,很多跨平台方案也可以运用,还有特殊高性能场景下我们也能够使用
使用 C-API 进行 UI 布局ArkUI 提供了一系列接口,能够让开发者通过 C/C++ 以「命令式」进行 UI 控件的需求开发,目前支持的内容有 UI 布局、UI 组件生成、动画、弹窗、响应事件等等。下面我们以官方的例子,让我们对 C-API 的了解更近一步。
创建鸿蒙 C++ 工程运用 C-API,首先我们还是要创建一个 NativeC++ 工程:
工程生成后就是一个标准 C++ 工程,因为本次的重点是如何使用 C-API 进行 UI 的布局,所以这里有关 napi 的介绍我们就不再一一列举了,笔者之前在 Harmony OS 应用开发 - 如何迁移 Crash 监控[1] 这篇文章中,有对 napi 进行过介绍。
导入链接库我们需要鸿蒙的 napi 能力以及 C-API 能力,因此需要在 CMake 中链接以下两个库 libace_napi.z.so libace_ndk.z.so
target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so)
创建 Native 控件三部曲查询获取 ArkUI 接口对象在 C-API 中,提供了以下四种枚举给开发者,开发者需要通过 OH_ArkUI_GetModuleInterface 这个接口查询获得支持的能力
typedef enum {
/** API related to UI components. For details, see the struct definition in arkui/native_node.h. */
ARKUI_NATIVE_NODE,
/** API related to dialog boxes. For details, see the struct definition in arkui/native_dialog.h. */
ARKUI_NATIVE_DIALOG,
/** API related to gestures. For details, see the struct definition in arkui/native_gesture.h. */
ARKUI_NATIVE_GESTURE,
/** API related to animations. For details, see the struct definition in arkui/native_animate.h.*/
ARKUI_NATIVE_ANIMATE,
} ArkUI_NativeAPIVariantKind;
OH_ArkUI_GetModuleInterface 接受 3 个参数,第一个是需要调用能力的枚举值,即我们上文提到的 ArkUI_NativeAPIVariantKind,第二个是 ArkUI 中关于对应能力的版本类型,这里官方目前是 「ArkUI_NativeNodeAPI_1」 ,第三个参数是系统返回给我们的一个句柄,后续我们创建一些控件,比如创建一个 Text 都需要它,因此我们都会通过传入一个指针获取
OH_ArkUI_GetModuleInterface(nativeAPIVariantKind, structType, structPtr)
比如我们想要在 Native 侧进行生成一个 UI 控件
ArkUI_NativeNodeAPI_1 *arkUINativeNodeApi_ = nullptr;
OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi_);
创建一个 Native 控件对象比如我们想要生成一个 Native 侧的控件 Text,我们可以通过第一步获取的 arkUINativeNodeApi_调用它的 createNode 方法,即可获得一个 Text 控件的 Native 句柄(后续可以操作 Text 的属性,比如背景、颜色等等),createNode 方法接受一个参数,这个参数代表着我们即将创建的控件类型
/** Custom node. */
ARKUI_NODE_CUSTOM = 0,
/** Text. */
ARKUI_NODE_TEXT = 1,
/** Text span. */
ARKUI_NODE_SPAN = 2,
/** Image span. */
ARKUI_NODE_IMAGE_SPAN = 3,
/** Image. */
ARKUI_NODE_IMAGE = 4,
/** Toggle. */
ARKUI_NODE_TOGGLE = 5,
createNode 返回一个 ArkUI_NodeHandle 类型的对象,这个对象用于我们后续对 Text 的属性进行操作,大家可以简单理解为 Text 的 Native 句柄
ArkUI_NodeHandle textHandle = arkUINativeNodeApi_ - createNode(ARKUI_NODE_TEXT)
设置 Native 控件的属性有了 textHandle ,我们就可以通过 arkUINativeNodeApi_ 对象的 setAttribute 方法设置对应的属性,属性也是一个枚举(在 native_node.h 中),比如我们设置了 Text 的 NODE_FONT_SIZE,即字体大小
void SetFontSize(float fontSize) {
assert(handle_);
// 创建一个中间对象,ArkUI_NumberValue 包含者float double等native类型,这里我们设置想要的数值即可
ArkUI_NumberValue value[] = {{.f32 = fontSize}};
ArkUI_AttributeItem item = {value, 1};
// 设置Text的大小
arkUINativeNodeApi_-setAttribute(textHandle, NODE_FONT_SIZE,
}
通过这一步,我们就创建了一个 Text 控件,并设置了它的一些属性,如字体大小。但是我们仅仅只是创建了这么一个对象,我们「还没有把它跟当前展示的 UI 绑定起来」
绑定创建好的控件到当前 UI 中使用 ndk 创建的控件也并不能完全脱离 ArkTS,我们还需要在 ArkTS 侧创建一个占位的控件,即 ContentSlot,ContentSlot 后续还会接受一个 NodeContent 对象,我们的 Native 侧创建的控件,就会被挂载在这个控件当中
import { NodeContent } from '@kit.ArkUI';
@Entry
@Component
struct Index {
// 初始化NodeContent对象。
private rootSlot = new NodeContent();
aboutToAppear(): void {
需要调用一个napi方法,把NodeContent对象通过napi传递给C++侧,这样C++的控件才能与当前UI进行绑定
nativeNode.createNativeRoot(this.rootSlot)
}
build() {
Column() {
Row() {
// 将NodeContent和ContentSlot占位组件绑定。
ContentSlot(this.rootSlot)
}.layoutWeight(1).onApear
}
.width('100%')
.height('100%')
}
}
我们选择在合适的时候调用一个 napi 方法,用于把 NodeContent 对象通过 napi 传递到 C++ 侧,后续我们创建的控件就依靠这个对象添加到当前的 UI 树中
napi_value CreateNativeRoot(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取NodeContent
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
return nullptr;
}
通过 OH_ArkUI_GetNodeContentFromNapiValue 接口,我们获取到 ArkTS 侧传递的 NodeContent 对象,这个对象就非常关键了,后续需要绑定 UI 控件的地方都需要这个对象。
添加自定义控件到组件树中通过 contentHandle 对象,我们拿到了当前 UI 的上下文,接着我们可以调用 OH_ArkUI_NodeContent_AddNode 方法,把我们上文创建好的 Text 组件进行绑定到对应的 UI 树中
OH_ArkUI_NodeContent_AddNode(contentHandle, textHandle);
同样的,我们也封装好一些对外的方法,类似 SetFontSize ,本质都是通过 setAttribute 对 textHandle 进行对应的属性设置
// 创建单一文本
std::string content = "我是来自native创建的Text";
auto textNode = std::make_sharedArkUITextNode
textNode-SetTextContent(content);
textNode-SetFontSize(16);
textNode-SetPercentWidth(1);
textNode-SetHeight(100);
textNode-SetBackgroundColor(0xFFfffacd);
textNode-SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
OH_ArkUI_NodeContent_AddNode(contentHandle, textHandle);
最终,我们完成了一个 Native Text 上屏的操作:
当然,更多更加复杂的场景,比如动画、事件响应,父子布局等均可以通过类似的 api 实现,我们可以通过官网[2]查看对用的 API
总结通过本文,我们初步了解到如何通过 C-API 的方式创建一个 Native 的 Text 并把这个 Text 与 ArkTS 侧进行了绑定,在这个过程中,我们涉及了大量的 C++ 接口以及大量的枚举场景,因此日常开发中,如果不是特殊的需求,比如封装跨平台方案等,笔者不太建议使用者直接使用这些接口创建 UI 控件。一方面是 C++ 与 NAPI 有一定的学习成本,同时维护成本相对于普通 ArkTS 编写 UI 来说也会上升,希望对大家有帮助!
Reference[1]https://juejin.cn/post/7292044286757470260: https://juejin.cn/post/7292044286757470260
[2]https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-use-ndk-V5: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.huawei.com%2Fconsumer%2Fcn%2Fdoc%2Fharmonyos-guides-V5%2Farkts-use-ndk-V5
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线