OpenHarmony实战开发-如何实现自定义绘制 (XComponent)

XComponent组件作为一种绘制组件,通常用于满足开发者较为复杂的自定义绘制需求,例如相机预览流的显示和游戏画面的绘制。

其可通过指定其type字段来实现不同的功能,主要有两个“surface”和“component”字段可供选择。

对于“surface”类型,开发者可将相关数据传入XComponent单独拥有的“NativeWindow”来渲染画面。

对于“component”类型,主要用于实现动态加载显示内容的目的。

surface类型

XComponent设置为surface类型时,通常用于EGL/OpenGLES和媒体数据写入,并将其显示在XComponent组件上。

设置为“surface“类型时XComponent组件可以和其他组件一起进行布局和渲染。

同时XComponent又拥有单独的“NativeWindow“,可以为开发者在native侧提供native window用来创建EGL/OpenGLES环境,进而使用标准的OpenGL ES开发。

除此之外,媒体相关应用(视频、相机等)也可以将相关数据写入XComponent所提供的NativeWindow,从而呈现相应画面。

使用EGL/OpenGLES渲染

native侧代码开发要点

应用如果要通过js来桥接native,一般需要使用napi接口来处理js交互,XComponent同样不例外。

Native侧处理js逻辑的文件类型为so:

  • 每个模块对应一个so
  • so的命名规则为 lib{模块名}.so

对于使用XComponent进行标准OpenGL ES开发的场景,CMAKELists.txt文件内容大致如下:

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,SHARED表示动态库
add_library(nativerender SHAREDxxx.cpp)# 查找相关库 (包括OpenGL ES相关库和XComponent提供的ndk接口)
find_library( EGL-libEGL )find_library( GLES-libGLESv3 )find_library( libace-libace_ndk.z )# 编译so所需要的依赖
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${libace-lib} libace_napi.z.so libc++.a)
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,SHARED表示动态库
add_library(nativerender SHAREDxxx.cpp)# 查找相关库 (包括OpenGL ES相关库和XComponent提供的ndk接口)
find_library( EGL-libEGL )find_library( GLES-libGLESv3 )find_library( libace-libace_ndk.z )# 编译so所需要的依赖
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${libace-lib} libace_napi.z.so libc++.a)

Napi模块注册

static napi_value Init(napi_env env, napi_value exports)
{// 定义暴露在模块上的方法napi_property_descriptor desc[] ={// 通过DECLARE_NAPI_FUNCTION宏完成方法名的映射,这里就是将native侧的PluginRender::NapiChangeColor方法映射到ets侧的changeColor方法DECLARE_NAPI_FUNCTION("changeColor", PluginRender::NapiChangeColor),};// 通过此接口开发者可在exports上挂载native方法(即上面的PluginRender::NapiChangeColor),exports会通过js引擎绑定到js层的一个js对象NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));return exports;
}static napi_module nativerenderModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init, // 指定加载对应模块时的回调函数.nm_modname = "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致.nm_priv = ((void*)0),.reserved = { 0 },
};extern "C" __attribute__((constructor)) void RegisterModule(void)
{// 注册so模块napi_module_register(&nativerenderModule);
}
c++
static napi_value Init(napi_env env, napi_value exports)
{// 定义暴露在模块上的方法napi_property_descriptor desc[] ={// 通过DECLARE_NAPI_FUNCTION宏完成方法名的映射,这里就是将native侧的PluginRender::NapiChangeColor方法映射到ets侧的changeColor方法DECLARE_NAPI_FUNCTION("changeColor", PluginRender::NapiChangeColor),};// 通过此接口开发者可在exports上挂载native方法(即上面的PluginRender::NapiChangeColor),exports会通过js引擎绑定到js层的一个js对象NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));return exports;
}static napi_module nativerenderModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init, // 指定加载对应模块时的回调函数.nm_modname = "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致.nm_priv = ((void*)0),.reserved = { 0 },
};extern "C" __attribute__((constructor)) void RegisterModule(void)
{// 注册so模块napi_module_register(&nativerenderModule);
}

解析XComponent组件的NativeXComponent实例

NativeXComponent为XComponent提供了在native层的实例,可作为js层和native层XComponent绑定的桥梁。XComponent所提供的的NDK接口都依赖于该实例。

可以在模块被加载时的回调内(即Napi模块注册中的Init函数)解析获得NativeXComponent实例

{// ...napi_status status;napi_value exportInstance = nullptr;OH_NativeXComponent *nativeXComponent = nullptr;// 用来解析出被wrap了NativeXComponent指针的属性status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);if (status != napi_ok) {return false;}// 通过napi_unwrap接口,解析出NativeXComponent的实例指针status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));// ...
}
c++
{// ...napi_status status;napi_value exportInstance = nullptr;OH_NativeXComponent *nativeXComponent = nullptr;// 用来解析出被wrap了NativeXComponent指针的属性status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);if (status != napi_ok) {return false;}// 通过napi_unwrap接口,解析出NativeXComponent的实例指针status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));// ...
}

注册XComponent事件回调

依赖解析XComponent组件的NativeXComponent实例拿到的NativeXComponent指针,通过OH_NativeXComponent_RegisterCallback接口进行回调注册。一般的会在模块被加载时的回调内(即Napi模块注册中的Init函数)进行回调注册。

{...OH_NativeXComponent *nativeXComponent = nullptr;// 解析出NativeXComponent实例OH_NativeXComponent_Callback callback;callback->OnSurfaceCreated = OnSurfaceCreatedCB; // surface创建成功后触发,开发者可以从中获取native window的句柄callback->OnSurfaceChanged = OnSurfaceChangedCB; // surface发生变化后触发,开发者可以从中获取native window的句柄以及XComponent的变更信息callback->OnSurfaceDestroyed = OnSurfaceDestroyedCB; // surface销毁时触发,开发者可以在此释放资源callback->DispatchTouchEvent = DispatchTouchEventCB; // XComponent的touch事件回调接口,开发者可以从中获得此次touch事件的信息OH_NativeXComponent_RegisterCallback(nativeXComponent, callback);...
}
c++
{...OH_NativeXComponent *nativeXComponent = nullptr;// 解析出NativeXComponent实例OH_NativeXComponent_Callback callback;callback->OnSurfaceCreated = OnSurfaceCreatedCB; // surface创建成功后触发,开发者可以从中获取native window的句柄callback->OnSurfaceChanged = OnSurfaceChangedCB; // surface发生变化后触发,开发者可以从中获取native window的句柄以及XComponent的变更信息callback->OnSurfaceDestroyed = OnSurfaceDestroyedCB; // surface销毁时触发,开发者可以在此释放资源callback->DispatchTouchEvent = DispatchTouchEventCB; // XComponent的touch事件回调接口,开发者可以从中获得此次touch事件的信息OH_NativeXComponent_RegisterCallback(nativeXComponent, callback);...
}

创建EGL/OpenGLES环境

在注册的OnSurfaceCreated回调中,开发者能拿到native window的句柄(其本质就是XComponent所单独拥有的NativeWindow),因此可以在这里创建应用自己的EGL/OpenGLES开发环境,由此开始具体渲染逻辑的开发。

EGLCore* eglCore_; // EGLCore为封装了OpenGL相关接口的类
uint64_t width_;
uint64_t height_;
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);if (ret === OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {eglCore_->GLContextInit(window, width_, height_); // 初始化OpenGL环境}
}
c++
EGLCore* eglCore_; // EGLCore为封装了OpenGL相关接口的类
uint64_t width_;
uint64_t height_;
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);if (ret === OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {eglCore_->GLContextInit(window, width_, height_); // 初始化OpenGL环境}
}

ArkTS侧语法介绍

开发者在ArkTS侧使用如下代码,即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。

XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' }).onLoad((context) => {}).onDestroy(() => {})
  • id :与XComponent组件为一一对应关系,不建议重复。通常开发者可以在native侧通过OH_NativeXComponent_GetXComponentId接口来获取对应的id从而绑定对应的XComponent。

说明:

如果id重复,在native侧将无法对多个XComponent进行区分。

  • libraryname:加载模块的名称,必须与在native侧Napi模块注册时nm_modname的名字一致。

说明:

应用加载模块实现跨语言调用有两种方式:

1.使用NAPI的import方式加载:

import nativerender from "libnativerender.so"

2.使用XComponent组件加载,本质也是使用了NAPI机制来加载。 该加载方式和import加载方式的区别在于,在加载动态库时会将XComponent的NativeXComponent实例暴露到应用的native层中,从而让开发者可以使用XComponent的NDK接口。

  • onLoad事件
  • 触发时刻:XComponent准备好surface后触发。
  • 参数context:其上面挂载了暴露在模块上的native方法,使用方法类似于利用 import context2 from“libnativerender.so” 直接加载模块后获得的context2实例。
  • 时序:onLoad事件的触发和Surface相关,其和native侧的OnSurfaceCreated的时序如下图:

图片2

  • onDestroy事件

触发时刻:XComponent组件被销毁时触发与一般ArkUI的组件销毁时机一致,其和native侧的OnSurfaceDestroyed的时序如下图:

图片3

媒体数据写入

XComponent所持有的NativeWindow符合“生产者-消费者”模型。

Camera、AVPlayer等符合生产者设计的部件都可以将数据写入XComponent持有的NativeWindow并通过XComponent显示。

图片1

开发者可通过绑定XComponentController获得对应XComponent的surfaceId(该id可以唯一确定一个surface),从而传给相应的部件接口。

class suf{surfaceId:string = "";mXComponentController: XComponentController = new XComponentController();set(){this.surfaceId = this.mXComponentController.getXComponentSurfaceId()}
}
@State surfaceId:string = "";
mXComponentController: object = new XComponentController();
XComponent({ id: '', type: 'surface', controller: this.mXComponentController }).onLoad(() => {let sufset = new suf()sufset.set()})

component类型

XComponent设置为component类型时通常用于在XComponent内部执行非UI逻辑以实现动态加载显示内容的目的。

说明:

type为"component"时,XComponent作为容器,子组件沿垂直方向布局:

  • 垂直方向上对齐格式:FlexAlign.Start
  • 水平方向上对齐格式:FlexAlign.Center

不支持所有的事件响应。

布局方式更改和事件响应均可通过挂载子组件来设置。

内部所写的非UI逻辑需要封装在一个或多个函数内。

场景示例

@Builder
function addText(label: string): void {Text(label).fontSize(40)
}@Entry
@Component
struct Index {@State message: string = 'Hello XComponent'@State messageCommon: string = 'Hello World'build() {Row() {Column() {XComponent({ id: 'xcomponentId-container', type: 'component' }) {addText(this.message)Divider().margin(4).strokeWidth(2).color('#F1F3F5').width("80%")Column() {Text(this.messageCommon).fontSize(30)}}}.width('100%')}.height('100%')}
}

在这里插入图片描述

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程》以及《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/829572.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AcrelEMS-MH民航机场智慧能源管平台解决方案【可靠供电/降低能耗/高效运维】

民航机场行业背景 自2012年以来&#xff0c;我国民航运输规模出现了显著增长&#xff0c;旅客运输量&#xff1a;从2012年的3.19亿人次上升至2019年的6.6亿人次&#xff08;注&#xff1a;为剔除疫情影响&#xff0c;此处采取疫情前2019年的数据&#xff0c;下同&#xff09;&…

java:SpringBootWeb请求响应

Servlet 用java编写的服务器端程序 客户端发送请求至服务器 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器 服务器将响应返回给客户端 javaweb的工作原理 在SpringBoot进行web程序开发时,内置了一个核心的Servlet程序DispatcherServlet,称之…

【小迪安全2023】第59天:服务攻防-中间件安全CVE复现lSApacheTomcatNginx

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

SQL dialect is not configured. Apache Cassandra matches best.没有配置SQL方言 如何处理

我这里是MySQL语言,所以我设置MySQL dialect 写个记录,之后更换全局SQL语言再换 下图是设置

boot https ssl 使用http协议访问报错

在springboot中配置ssl以后&#xff0c; 再次使用http访问对应的接口就会报错 可以考虑如下设置&#xff0c;将http访问的端口重定向到https对应的端口 import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.tomcat.util…

神经网络中多层卷积的作用

在神经网络中采用多层卷积的目的是为了逐步提取和组合图像的抽象特征&#xff0c;从而更有效地学习数据的表示并执行复杂的任务。不同层的卷积具有不同的作用&#xff0c;从较低层次的特征&#xff08;例如边缘、纹理&#xff09;到较高层次的抽象特征&#xff08;例如物体部件…

ZISUOJ 数据结构--串及其应用

说明&#xff1a; 都是字符串的基本操作没啥好说的&#xff0c;直接上题目和代码了。 题目列表&#xff1a; 问题 A: 字符串翻转 参考题解&#xff1a; #include <iostream> #include <string> #include <algorithm> using std::cin; using std::cout; usi…

OpenHarmony语言基础类库【@ohos.util.PlainArray (非线性容器PlainArray)】

PlainArray可用于存储具有关联关系的key-value键值对集合&#xff0c;存储元素中key值唯一&#xff0c;key值类型为number类型&#xff0c;每个key对应一个value。 PlainArray依据泛型定义&#xff0c;采用轻量级结构&#xff0c;集合中key值的查找依赖于二分查找算法&#xf…

数字电路-可预置倒计时器Multisim仿真

数字电路之于FPGA意义重大。本可预置倒计时器设计采用40106作为振荡电路&#xff0c;由74LSl92、74LS47D和七段共阴数码管构成计时电路&#xff0c;具有启动/预置、暂停/继续计时和报警功能。紫色文字是超链接&#xff0c;点击自动跳转至相关博文。持续更新&#xff0c;原创不易…

用C实现通讯录(详细讲解+源码)

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL.. &#x1f4da;以后会将数据结构收录为一个系列&#xff0c;敬请期待 ● 本期内容会给大家带来通讯录的讲解&#xff0c;主要是利用结构体来实现通讯录&#xff0c;该通讯…

[ESP32]:TFLite Micro推理CIFAR10模型

[ESP32]&#xff1a;TFLite Micro推理CIFAR10模型 模型训练 数据集处理 from keras.datasets import cifar10 from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, load_model, Model from keras.layers import Input, Dense, …

xLua详解

目录 环境准备xLua导入 C#调用LuaLua解析器Lua文件加载重定向Lua解析管理器全局变量的获取全局函数的获取List和Dictionary映射table类映射table接口映射tableLuaTable映射table Lua调用C#准备工作Lua使用C#类Lua调用C#枚举Lua使用C# 数组 List 字典数组List字典 Lua使用C#扩展…

解决NetworkManager覆盖/etc/resolv.conf的问题

发布时间&#xff1a;2024.4.27 问题 /etc/resolv.conf是Linux下DNS的配置文件。 但是NetworkManager会用覆盖它&#xff0c;导致我们每次都要重新配置。 解决办法 这是官方推荐的做法。或者你可以用resolveconf工具。 $ nm-connection-editor会调起一个界面&#xff0c;…

Python_AI库 matplotlib扩展知识

Python_AI库 matplotlib扩展知识 在数据分析和处理的领域里&#xff0c;可视化是一种不可或缺的手段。通过图形化的展示&#xff0c;我们可以更直观地理解数据的分布、趋势和关系。而matplotlib&#xff0c;作为Python中最为流行的数据可视化库之一&#xff0c;以其强大的功能…

【C++】简易二叉搜索树

目录 一、概念&#xff1a; 二、代码实现&#xff1a; 大致结构&#xff1a; 1、遍历&#xff1a; 2、insert 3、find 4、erase 三、总结&#xff1a; 一、概念&#xff1a; 二叉搜索树又称为二叉排序树&#xff0c;是一种具有特殊性质的二叉树&#xff0c;对于每一个节…

在虚拟环境中找到Qt Designer

Pyqt5中找到Qt Designer 安装Pyqt5和Qt Designer: pip install pyqt5-tools 假设Python的虚拟环境名为:d2l &#xff0c;虚拟环境在d2l文件夹中 D:\Software\d2l\Lib\site-packages\qt5_applications\Qt\bin 双击Qt designer启动 Pyside2中找到Qt Designer d2l是虚拟环境…

上位机图像处理和嵌入式模块部署(树莓派4b下使用sqlite3)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 嵌入式设备下面&#xff0c;有的时候也要对数据进行处理和保存。如果处理的数据不是很多&#xff0c;一般用json就可以。但是数据如果量比较大&…

rust前端web开发框架yew使用

构建完整基于 rust 的 web 应用,使用yew框架 trunk 构建、打包、发布 wasm web 应用 安装后会作为一个系统命令&#xff0c;默认有两个特性开启 rustls - 客户端与服务端通信的 tls 库update_check - 用于应用启动时启动更新检查&#xff0c;应用有更新时提示用户更新。nati…

Linux——终端

一、终端 1、终端是什么 终端最初是指终端设备&#xff08;Terminal&#xff09;&#xff0c;它是一种用户与计算机系统进行交互的硬件设备。在早期的计算机系统中&#xff0c;终端通常是一台带有键盘和显示器的电脑&#xff0c;用户通过它输入命令&#xff0c;计算机在执行命…

SpringBoot引入Layui样式总是出现404

一般出现Layui样式文件如css&#xff0c;js404的错误 解决方案 &#xff08;1&#xff09;首先将其中的静态资源下载resources/static中 &#xff08;2&#xff09;在启动类中重写方法 package com.gq.booksystem;import org.mybatis.spring.annotation.MapperScan; import …