XComponent
组件作为一种绘制组件,通常用于满足用户复杂的自定义绘制需求,其主要有两种类型"surface和component。对于surface类型可以将相关数据传入XComponent单独拥有的NativeWindow来渲染画面。
由于上层UI是采用arkTS开发,那么想要使用XComponent的话,就需要一个桥接和native层交互,类似Android的JNI,鸿蒙应用可以使用napi接口来处理js和native层的交互。
1. 编写CMAKELists.txt文件
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(XComponent) #项目名称
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
#头文件查找路径
include_directories(
${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
)
# 编译目标so动态库
add_library(nativerender SHARED
samples/minute_view.cpp
plugin/plugin_manager.cpp
common/HuMarketMinuteData.cpp
common/MinuteItem.cpp
napi_init.cpp
)
# 查找需要的公共库
find_library(
# Sets the name of the path variable.
hilog-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
hilog_ndk.z
)
#编译so所需要的依赖
target_link_libraries(nativerender PUBLIC
libc++.a
${hilog-lib}
libace_napi.z.so
libace_ndk.z.so
libnative_window.so
libnative_drawing.so
)
2. Napi模块注册
#include <hilog/log.h>
#include "plugin/plugin_manager.h"
#include "common/log_common.h"
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
if ((nullptr == env) || (nullptr == exports)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
return nullptr;
}
PluginManager::GetInstance()->Export(env, exports);
return exports;
}
EXTERN_C_END
static napi_module nativerenderModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "nativerender",
.nm_priv = ((void *)0),
.reserved = {
0 }
};
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&nativerenderModule);
}
定义napi_module信息,里面包含模块名称(和arkts侧使用时的libraryname要保持一致),加载模块时的回调函数即Init()。然后注册so模块napi_module_register(&nativerenderModule), 接口Init()函数会收到回调,在Init()有两个参数:
- env: napi上下文环境;
- exports: 用于挂载native函数将其导出,会通过js引擎绑定到js层的一个js对象;
3.解析XComponent组件的NativeXComponent实例
if ((env == nullptr) || (exports == nullptr)) {
DRAWING_LOGE("Export: env or exports is null");
return;
}
napi_value exportInstance = nullptr;
// 用来解析出被wrap了NativeXComponent指针的属性
if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
DRAWING_LOGE("Export: napi_get_named_property fail");
return;
}
OH_NativeXComponent *nativeXComponent = nullptr;
// 通过napi_unwrap接口,解析出NativeXComponent的实例指针
if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
DRAWING_LOGE("Export: napi_unwrap fail");
return;
}
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {
'\0'};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
DRAWING_LOGE("Export: OH_NativeXComponent_GetXComponentId fail");
return;
}
4.注册XComponent事件回调
void MinuteView::RegisterCallback(OH_NativeXComponent *nativeXComponent) {
DRAWING_LOGI("register callback");
renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
renderCallback_.DispatchTouchEvent = nullptr;
renderCallback_.OnSurfaceChanged = OnSurfaceChanged;
// 注册XComponent事件回调
OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_);
}
在事件回调中可以获取组件的宽高信息:
tatic void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
DRAWING_LOGI("OnSurfaceCreatedCB");
if ((component == nullptr) || (window == nullptr)) {
DRAWING_LOGE("OnSurfaceCreatedCB: component or window is null");
return;
}
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {
'\0'};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
DRAWING_LOGE("OnSurfaceCreatedCB: Unable to get XComponent id");
return