当前位置: 代码网 > it编程>编程语言>Javascript > HarmonyOS Next开发学习手册——Native XComponent

HarmonyOS Next开发学习手册——Native XComponent

2024年08月06日 Javascript 我要评论
Native XComponent是XComponent组件提供在Native层的实例,可作为JS层和Native层XComponent绑定的桥梁。XComponent所提供的NDK接口都依赖于该实例。接口能力包括获取Native Window实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。

场景介绍

native xcomponent是xcomponent组件提供在native层的实例,可作为js层和native层xcomponent绑定的桥梁。xcomponent所提供的ndk接口都依赖于该实例。接口能力包括获取native window实例、获取xcomponent的布局/事件信息、注册xcomponent的生命周期回调、注册xcomponent的触摸、鼠标、按键等事件回调。针对native xcomponent,主要的开发场景如下:

  • 利用native xcomponent提供的接口注册xcomponent的生命周期和事件回调。
  • 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。
  • 利用native window和egl接口开发自定义绘制内容以及申请和提交buffer到图形队列。

接口说明

接口名描述
oh_nativexcomponent_getxcomponentid(oh_nativexcomponent* component, char* id, uint64_t* size)获取xcomponent的id。
oh_nativexcomponent_getxcomponentsize(oh_nativexcomponent* component, const void* window, uint64_t* width, uint64_t* height)获取xcomponent持有的surface的大小。
oh_nativexcomponent_getxcomponentoffset(oh_nativexcomponent* component, const void* window, double* x, double* y)获取xcomponent持有的surface相对其父组件左顶点的偏移量。
oh_nativexcomponent_gettouchevent(oh_nativexcomponent* component, const void* window, oh_nativexcomponent_touchevent* touchevent)获取由xcomponent触发的触摸事件。touchevent内的具体属性值可参考oh_nativexcomponent_touchevent
oh_nativexcomponent_gettouchpointtooltype(oh_nativexcomponent* component, uint32_t pointindex, oh_nativexcomponent_touchpointtooltype* tooltype)获取xcomponent触摸点的工具类型。
oh_nativexcomponent_gettouchpointtiltx(oh_nativexcomponent* component, uint32_t pointindex, float* tiltx)获取xcomponent触摸点处相对x轴的倾斜角度。
oh_nativexcomponent_gettouchpointtilty(oh_nativexcomponent* component, uint32_t pointindex, float* tilty)获取xcomponent触摸点处相对y轴的倾斜角度。
oh_nativexcomponent_getmouseevent(oh_nativexcomponent* component, const void* window, oh_nativexcomponent_mouseevent* mouseevent)获取由xcomponent触发的鼠标事件。
oh_nativexcomponent_registercallback(oh_nativexcomponent* component, oh_nativexcomponent_callback* callback)为此oh_nativexcomponent实例注册生命周期和触摸事件回调。
oh_nativexcomponent_registermouseeventcallback(oh_nativexcomponent* component, oh_nativexcomponent_mouseevent_callback* callback)为此oh_nativexcomponent实例注册鼠标事件回调。
oh_nativexcomponent_registerfocuseventcallback(oh_nativexcomponent* component, void (callback)(oh_nativexcomponent component, void* window))为此oh_nativexcomponent实例注册获得焦点事件回调。
oh_nativexcomponent_registerkeyeventcallback(oh_nativexcomponent* component, void (callback)(oh_nativexcomponent component, void* window))为此oh_nativexcomponent实例注册按键事件回调。
oh_nativexcomponent_registerblureventcallback(oh_nativexcomponent* component, void (callback)(oh_nativexcomponent component, void* window))为此oh_nativexcomponent实例注册失去焦点事件回调。
oh_nativexcomponent_getkeyevent(oh_nativexcomponent* component, oh_nativexcomponent_keyevent** keyevent)获取由xcomponent触发的按键事件。
oh_nativexcomponent_getkeyeventaction(oh_nativexcomponent_keyevent* keyevent, oh_nativexcomponent_keyaction* action)获取按键事件的动作。
oh_nativexcomponent_getkeyeventcode(oh_nativexcomponent_keyevent* keyevent, oh_nativexcomponent_keycode* code)获取按键事件的键码值。
oh_nativexcomponent_getkeyeventsourcetype(oh_nativexcomponent_keyevent* keyevent, oh_nativexcomponent_eventsourcetype* sourcetype)获取按键事件的输入源类型。
oh_nativexcomponent_getkeyeventdeviceid(oh_nativexcomponent_keyevent* keyevent, int64_t* deviceid)获取按键事件的设备id。
oh_nativexcomponent_getkeyeventtimestamp(oh_nativexcomponent_keyevent* keyevent, int64_t* timestamp)获取按键事件的时间戳。

生命周期说明

开发者在arkts侧使用如下代码即可用xcomponent组件进行利用egl/opengles渲染的开发。

@builder
function mycomponent() {
  xcomponent({ id: 'xcomponentid1', type: 'surface', libraryname: 'nativerender' })
    .onload((context) => {})
    .ondestroy(() => {})
}

onload事件

触发时刻:xcomponent准备好surface后触发。

参数context:其上面挂载了暴露在模块上的native方法,使用方法类似于利用 import context from “libnativerender.so” 直接加载模块后获得的context实例。

时序:onload事件的触发和surface相关,其和native侧的onsurfacecreated的时序如下图:

ondestroy事件

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

开发步骤

以下步骤描述了如何使用xcomponent组件调用napi接口来创建egl/gles环境,实现在主页面绘制图形,并可以改变图形的颜色。

  1. 在界面中定义xcomponent
@entry
@component
struct index {
    @state message: string = 'hello world'
    xcomponentcontext: object | undefined = undefined;
    xcomponentattrs: xcomponentattrs = {
        id: 'xcomponentid',
        type: xcomponenttype.surface,
        libraryname: 'nativerender'
    }

    build() {
        row() {
        // ...
        // 在xxx.ets 中定义 xcomponent
        xcomponent(this.xcomponentattrs)
            .focusable(true) // 可响应键盘事件
            .onload((xcomponentcontext) => {
            this.xcomponentcontext = xcomponentcontext;
            })
            .ondestroy(() => {
            console.log("ondestroy");
            })
        // ...
        }
        .height('100%')
    }
}

interface xcomponentattrs {
    id: string;
    type: number;
    libraryname: string;
}
  1. napi模块注册,具体使用请参考 native api在应用工程中的使用指导 。
// 在napi_init.cpp文件中,init方法注册接口函数,从而将封装的c++方法传递出来,供js侧调用
extern_c_start
static napi_value init(napi_env env, napi_value exports)
{
    // ...
    // 向js侧暴露接口getcontext()
    napi_property_descriptor desc[] = {
        { "getcontext", nullptr, pluginmanager::getcontext, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
        oh_log_print(log_app, log_error, log_print_domain, "init", "napi_define_properties failed");
        return nullptr;
    }
    // 方法内检查环境变量是否包含xcomponent组件实例,若实例存在注册绘制相关接口
    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 }
};

// __attribute__((constructor))修饰的方法由系统自动调用,使用napi接口napi_module_register()传入模块描述信息进行模块注册
extern "c" __attribute__((constructor)) void registermodule(void)
{
    napi_module_register(&nativerendermodule);
}

// 使用napi中的napi_define_properties方法,向js侧暴露drawpattern()方法,在js侧调用drawpattern()来绘制内容。
void pluginrender::export(napi_env env, napi_value exports)
{
    // ...
    // 将接口函数注册为js侧接口drawpattern
    napi_property_descriptor desc[] = {
        { "drawpattern", nullptr, pluginrender::napidrawpattern, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
        oh_log_print(log_app, log_error, log_print_domain, "pluginrender", "export: napi_define_properties failed");
    }
}
  1. 注册xcomponent事件回调,使用napi实现xcomponent事件回调函数。

    (1) 定义surface创建成功,发生改变,销毁和xcomponent的touch事件回调接口。

// 定义一个函数onsurfacecreatedcb(),封装初始化环境与绘制背景
void onsurfacecreatedcb(oh_nativexcomponent *component, void *window)
{
    // ...
    // 获取xcomponent的id,即js侧xcomponent组件构造中的id参数
    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) {
        oh_log_print(log_app, log_error, log_print_domain, "callback",
            "onsurfacecreatedcb: unable to get xcomponent id");
        return;
    }

    // 初始化环境与绘制背景
    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    uint64_t width;
    uint64_t height;
    // 获取xcomponent拥有的surface的大小
    int32_t xsize = oh_nativexcomponent_getxcomponentsize(component, window, &width, &height);
    if ((xsize == oh_nativexcomponent_result_success) && (render != nullptr)) {
        if (render->eglcore_->eglcontextinit(window, width, height)) {
            render->eglcore_->background();
        }
    }
}

// 定义一个函数onsurfacechangedcb()
void onsurfacechangedcb(oh_nativexcomponent *component, void *window)
{
    // ...
    // 获取xcomponent的id
    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) {
        oh_log_print(log_app, log_error, log_print_domain, "callback",
            "onsurfacechangedcb: unable to get xcomponent id");
        return;
    }

    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render != nullptr) {
        // 封装onsurfacechanged方法
        render->onsurfacechanged(component, window);
    }
}

// 定义一个函数onsurfacedestroyedcb(),将pluginrender类内释放资源的方法release()封装在其中
void onsurfacedestroyedcb(oh_nativexcomponent *component, void *window)
{
    // ...
    // 获取xcomponent的id
    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) {
        oh_log_print(log_app, log_error, log_print_domain, "callback",
            "onsurfacedestroyedcb: unable to get xcomponent id");
        return;
    }

    std::string id(idstr);
    // 释放资源
    pluginrender::release(id);
}

// 定义一个函数dispatchtoucheventcb(),响应触摸事件时触发该回调
void dispatchtoucheventcb(oh_nativexcomponent *component, void *window)
{
    // ...
    // 获取xcomponent的id
    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) {
        oh_log_print(log_app, log_error, log_print_domain, "callback",
            "dispatchtoucheventcb: unable to get xcomponent id");
        return;
    }

    std::string id(idstr);
    pluginrender *render = pluginrender::getinstance(id);
    if (render != nullptr) {
        // 封装ontouchevent方法
        render->ontouchevent(component, window);
    }
}

// 定义一个函数dispatchmouseeventcb(),响应鼠标事件时触发该回调
void dispatchmouseeventcb(oh_nativexcomponent *component, void *window) {
    oh_log_print(log_app, log_info, log_print_domain, "callback", "dispatchmouseeventcb");
    int32_t ret;
    char idstr[oh_xcomponent_id_len_max + 1] = {};
    uint64_t idsize = oh_xcomponent_id_len_max + 1;
    ret = oh_nativexcomponent_getxcomponentid(component, idstr, &idsize);
    if (ret != oh_nativexcomponent_result_success) {
        return;
    }

    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render) {
        // 封装onmouseevent方法
        render->onmouseevent(component, window);
    }
}

// 定义一个函数dispatchhovereventcb(),响应鼠标悬停事件时触发该回调
void dispatchhovereventcb(oh_nativexcomponent *component, bool ishover) {
    oh_log_print(log_app, log_info, log_print_domain, "callback", "dispatchhovereventcb");
    int32_t ret;
    char idstr[oh_xcomponent_id_len_max + 1] = {};
    uint64_t idsize = oh_xcomponent_id_len_max + 1;
    ret = oh_nativexcomponent_getxcomponentid(component, idstr, &idsize);
    if (ret != oh_nativexcomponent_result_success) {
        return;
    }

    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render) {
        // 封装onhoverevent方法
        render->onhoverevent(component, ishover);
    }
}

// 定义一个函数onfocuseventcb(),响应获焦事件时触发该回调
void onfocuseventcb(oh_nativexcomponent *component, void *window) {
    oh_log_print(log_app, log_info, log_print_domain, "callback", "onfocuseventcb");
    int32_t ret;
    char idstr[oh_xcomponent_id_len_max + 1] = {};
    uint64_t idsize = oh_xcomponent_id_len_max + 1;
    ret = oh_nativexcomponent_getxcomponentid(component, idstr, &idsize);
    if (ret != oh_nativexcomponent_result_success) {
        return;
    }

    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render) {
        // 封装onfocusevent方法
        render->onfocusevent(component, window);
    }
}

// 定义一个函数onblureventcb(),响应失去焦点事件时触发该回调
void onblureventcb(oh_nativexcomponent *component, void *window) {
    oh_log_print(log_app, log_info, log_print_domain, "callback", "onblureventcb");
    int32_t ret;
    char idstr[oh_xcomponent_id_len_max + 1] = {};
    uint64_t idsize = oh_xcomponent_id_len_max + 1;
    ret = oh_nativexcomponent_getxcomponentid(component, idstr, &idsize);
    if (ret != oh_nativexcomponent_result_success) {
        return;
    }

    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render) {
        // 封装onblurevent方法
        render->onblurevent(component, window);
    }
}

// 定义一个函数onkeyeventcb(),响应按键事件时触发该回调
void onkeyeventcb(oh_nativexcomponent *component, void *window) {
    oh_log_print(log_app, log_info, log_print_domain, "callback", "onkeyeventcb");
    int32_t ret;
    char idstr[oh_xcomponent_id_len_max + 1] = {};
    uint64_t idsize = oh_xcomponent_id_len_max + 1;
    ret = oh_nativexcomponent_getxcomponentid(component, idstr, &idsize);
    if (ret != oh_nativexcomponent_result_success) {
        return;
    }
    std::string id(idstr);
    auto render = pluginrender::getinstance(id);
    if (render) {
        // 封装onkeyevent方法
        render->onkeyevent(component, window);
    }
}

// 定义一个onsurfacechanged()方法
void pluginrender::onsurfacechanged(oh_nativexcomponent* component, void* window)
{
    // ...
    std::string id(idstr);
    pluginrender* render = pluginrender::getinstance(id);
    double offsetx;
    double offsety;
    // 获取xcomponent持有的surface相对其父组件左顶点的偏移量
    oh_nativexcomponent_getxcomponentoffset(component, window, &offsetx, &offsety);
    oh_log_print(log_app, log_info, log_print_domain, "oh_nativexcomponent_getxcomponentoffset",
        "offsetx = %{public}lf, offsety = %{public}lf", offsetx, offsety);
    uint64_t width;
    uint64_t height;
    oh_nativexcomponent_getxcomponentsize(component, window, &width, &height);
    if (render != nullptr) {
        render->eglcore_->updatesize(width, height);
    }
}

// 定义一个ontouchevent()方法
void pluginrender::ontouchevent(oh_nativexcomponent* component, void* window)
{
    // ...
    oh_nativexcomponent_touchevent touchevent;
    // 获取由xcomponent触发的触摸事件
    oh_nativexcomponent_gettouchevent(component, window, &touchevent);
    // 获取xcomponent触摸点相对于xcomponent组件左边缘的坐标x和相对于xcomponent组件上边缘的坐标y
    oh_log_print(log_app, log_info, log_print_domain, "ontouchevent",
        "touch info: x = %{public}lf, y = %{public}lf", touchevent.x, touchevent.y);
    // 获取xcomponent触摸点相对于xcomponent所在应用窗口左上角的x坐标和相对于xcomponent所在应用窗口左上角的y坐标
    oh_log_print(log_app, log_info, log_print_domain, "ontouchevent",
        "touch info: screenx = %{public}lf, screeny = %{public}lf", touchevent.screenx, touchevent.screeny);
    std::string id(idstr);
    pluginrender* render = pluginrender::getinstance(id);
    if (render != nullptr && touchevent.type == oh_nativexcomponent_toucheventtype::oh_nativexcomponent_up) {
        render->eglcore_->changecolor();
        haschangecolor_ = 1;
    }
    float tiltx = 0.0f;
    float tilty = 0.0f;
    oh_nativexcomponent_touchpointtooltype tooltype =
        oh_nativexcomponent_touchpointtooltype::oh_nativexcomponent_tool_type_unknown;
    // 获取xcomponent触摸点的工具类型
    oh_nativexcomponent_gettouchpointtooltype(component, 0, &tooltype);
    // 获取xcomponent触摸点处相对x轴的倾斜角度
    oh_nativexcomponent_gettouchpointtiltx(component, 0, &tiltx);
    // 获取xcomponent触摸点处相对y轴的倾斜角度
    oh_nativexcomponent_gettouchpointtilty(component, 0, &tilty);
    oh_log_print(log_app, log_info, log_print_domain, "ontouchevent",
        "touch info: tooltype = %{public}d, tiltx = %{public}lf, tilty = %{public}lf", tooltype, tiltx, tilty);
}

// 定义一个onmouseevent()方法
void pluginrender::onmouseevent(oh_nativexcomponent *component, void *window) {
   oh_log_print(log_app, log_info, log_print_domain, "pluginrender", "onmouseevent");
   oh_nativexcomponent_mouseevent mouseevent;
   // 获取由xcomponent触发的鼠标事件
   int32_t ret = oh_nativexcomponent_getmouseevent(component, window, &mouseevent);
   if (ret == oh_nativexcomponent_result_success) {
       oh_log_print(log_app, log_info, log_print_domain, "pluginrender", "mouseevent info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d", mouseevent.x, mouseevent.y, mouseevent.action, mouseevent.button);
   } else {
       oh_log_print(log_app, log_error, log_print_domain, "pluginrender", "getmouseevent error");
   }
}

// 定义一个onmouseevent()方法
void pluginrender::onkeyevent(oh_nativexcomponent *component, void *window) {
   oh_log_print(log_app, log_info, log_print_domain, "pluginrender", "onkeyevent");

   oh_nativexcomponent_keyevent *keyevent = nullptr;
   // 获取由xcomponent触发的按键事件。
   if (oh_nativexcomponent_getkeyevent(component, &keyevent) >= 0) {
       oh_nativexcomponent_keyaction action;
       // 获取按键事件的动作
       oh_nativexcomponent_getkeyeventaction(keyevent, &action);
       oh_nativexcomponent_keycode code;
       // 获取按键事件的键码值
       oh_nativexcomponent_getkeyeventcode(keyevent, &code);
       oh_nativexcomponent_eventsourcetype sourcetype;
       // 获取按键事件的输入源类型
       oh_nativexcomponent_getkeyeventsourcetype(keyevent, &sourcetype);
       int64_t deviceid;
       // 获取按键事件的设备id
       oh_nativexcomponent_getkeyeventdeviceid(keyevent, &deviceid);
       int64_t timestamp;
       // 获取按键事件的时间戳
       oh_nativexcomponent_getkeyeventtimestamp(keyevent, &timestamp);
       oh_log_print(log_app, log_info, log_print_domain, "pluginrender", "keyevent info: action=%{public}d, code=%{public}d, sourcetype=%{public}d, deviceid=%{public}ld, timestamp=%{public}ld", action, code, sourcetype, deviceid, timestamp);
   } else {
       oh_log_print(log_app, log_error, log_print_domain, "pluginrender", "getkeyevent error");
   }
}

(2) 注册xcomponent事件回调函数,在xcomponent事件触发时调用3.1步骤中定义的方法。

void pluginrender::registercallback(oh_nativexcomponent *nativexcomponent) {
    // 设置组件创建事件的回调函数,组件创建时触发相关操作,初始化环境与绘制背景
    rendercallback_.onsurfacecreated = onsurfacecreatedcb;
    // 设置组件改变事件的回调函数,组件改变时触发相关操作
    rendercallback_.onsurfacechanged = onsurfacechangedcb;
    // 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源
    rendercallback_.onsurfacedestroyed = onsurfacedestroyedcb;
    // 设置触摸事件的回调函数,在触摸事件触发时调用napi接口函数,从而调用原c++方法
    rendercallback_.dispatchtouchevent = dispatchtoucheventcb;
    // 将oh_nativexcomponent_callback注册给nativexcomponent
    oh_nativexcomponent_registercallback(nativexcomponent, &rendercallback_);
    
    // 设置鼠标事件的回调函数,在触摸事件触发时调用napi接口函数,从而调用原c++方法
    mousecallback_.dispatchmouseevent = dispatchmouseeventcb;
    // 设置鼠标悬停事件的回调函数,在触摸事件触发时调用napi接口函数,从而调用原c++方法
    mousecallback_.dispatchhoverevent = dispatchhovereventcb;
    // 将oh_nativexcomponent_mouseevent_callback注册给nativexcomponent
    oh_nativexcomponent_registermouseeventcallback(nativexcomponent, &mousecallback_);
    
    // 将onfocuseventcb方法注册给nativexcomponent
    oh_nativexcomponent_registerfocuseventcallback(nativexcomponent, onfocuseventcb);
    // 将onkeyeventcb方法注册给nativexcomponent
    oh_nativexcomponent_registerkeyeventcallback(nativexcomponent, onkeyeventcb);
    // 将onblureventcb方法注册给 nativexcomponent
    oh_nativexcomponent_registerblureventcallback(nativexcomponent, onblureventcb);
}

(3) 定义napidrawpattern方法,暴露到js侧的drawpattern()方法会执行该方法。

napi_value pluginrender::napidrawpattern(napi_env env, napi_callback_info info)
{
    // ...
    // 获取环境变量参数
    napi_value thisarg;
    if (napi_get_cb_info(env, info, nullptr, nullptr, &thisarg, nullptr) != napi_ok) {
        oh_log_print(log_app, log_error, log_print_domain, "pluginrender", "napidrawpattern: napi_get_cb_info fail");
        return nullptr;
    }
   
    // 获取环境变量中xcomponent实例
    napi_value exportinstance;
    if (napi_get_named_property(env, thisarg, oh_native_xcomponent_obj, &exportinstance) != napi_ok) {
        oh_log_print(log_app, log_error, log_print_domain, "pluginrender",
            "napidrawpattern: napi_get_named_property fail");
        return nullptr;
    }
   
    // 通过napi_unwrap接口,获取xcomponent的实例指针
    oh_nativexcomponent *nativexcomponent = nullptr;
    if (napi_unwrap(env, exportinstance, reinterpret_cast<void **>(&nativexcomponent)) != napi_ok) {
        oh_log_print(log_app, log_error, log_print_domain, "pluginrender", "napidrawpattern: napi_unwrap fail");
        return nullptr;
    }
   
    // 获取xcomponent实例的id
    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) {
        oh_log_print(log_app, log_error, log_print_domain, "pluginrender",
            "napidrawpattern: unable to get xcomponent id");
        return nullptr;
    }
   
    std::string id(idstr);
    pluginrender *render = pluginrender::getinstance(id);
    if (render) {
        // 调用绘制方法
        render->eglcore_->draw();
        oh_log_print(log_app, log_info, log_print_domain, "pluginrender", "render->eglcore_->draw() executed");
    }
    return nullptr;
}
  1. 初始化环境,包括初始化可用的egldisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。
void eglcore::updatesize(int width, int height) 
{
    width_ = width;
    height_ = height;
    if (width_ > 0) {
        // 计算绘制矩形宽度百分比
        width_percent_ = fifty_percent * height_ / width_;
    }
}

bool eglcore::eglcontextinit(void *window, int width, int height)
{
    // ...
    updatesize(width, height);
    eglwindow_ = static_cast<eglnativewindowtype>(window);

    // 初始化display
    egldisplay_ = eglgetdisplay(egl_default_display);
    if (egldisplay_ == egl_no_display) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "eglgetdisplay: unable to get egl display");
        return false;
    }

    // 初始化egl
    eglint majorversion;
    eglint minorversion;
    if (!eglinitialize(egldisplay_, &majorversion, &minorversion)) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore",
            "eglinitialize: unable to get initialize egl display");
        return false;
    }

    // 选择配置
    const eglint maxconfigsize = 1;
    eglint numconfigs;
    if (!eglchooseconfig(egldisplay_, attrib_list, &eglconfig_, maxconfigsize, &numconfigs)) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "eglchooseconfig: unable to choose configs");
        return false;
    }

    // 创建环境
    return createenvironment();
}

bool eglcore::createenvironment()
{
    // ...
    // 创建surface
    eglsurface_ = eglcreatewindowsurface(egldisplay_, eglconfig_, eglwindow_, null);

    // ...
    // 创建context
    eglcontext_ = eglcreatecontext(egldisplay_, eglconfig_, egl_no_context, context_attribs);
    if (!eglmakecurrent(egldisplay_, eglsurface_, eglsurface_, eglcontext_)) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "eglmakecurrent failed");
        return false;
    }

    // 创建program
    program_ = createprogram(vertex_shader, fragment_shader);
    if (program_ == program_error) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "createprogram: unable to create program");
        return false;
    }
    return true;
}
  1. 渲染功能实现

(1) 绘制背景。

// 绘制背景颜色 #f4f4f4
const glfloat background_color[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };

// 绘制背景顶点
const glfloat background_rectangle_vertices[] = {
    -1.0f, 1.0f,
    1.0f, 1.0f,
    1.0f, -1.0f,
    -1.0f, -1.0f
};

// 绘制背景颜色
void eglcore::background()
{
    glint position = preparedraw();
    if (position == position_error) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "background get position failed");
        return;
    }

    if (!executedraw(position, background_color, background_rectangle_vertices,
        sizeof(background_rectangle_vertices))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "background execute draw failed");
        return;
    }

    if (!finishdraw()) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "background finishdraw failed");
        return;
    }
}

// 绘前准备,获取position,创建成功时position值从0开始
glint eglcore::preparedraw()
{
    if ((egldisplay_ == nullptr) || (eglsurface_ == nullptr) || (eglcontext_ == nullptr) ||
        (!eglmakecurrent(egldisplay_, eglsurface_, eglsurface_, eglcontext_))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "preparedraw: param error");
        return position_error;
    }

    glviewport(default_x_position, default_y_position, width_, height_);
    glclearcolor(gl_red_default, gl_green_default, gl_blue_default, gl_alpha_default);
    glclear(gl_color_buffer_bit);
    gluseprogram(program_);

    return glgetattriblocation(program_, position_name);
}

// 依据传入参数在指定区域绘制指定颜色
bool eglcore::executedraw(glint position, const glfloat *color, const glfloat shapevertices[],
    unsigned long vertsize)
{
    if ((position > 0) || (color == nullptr) || (vertsize / sizeof(shapevertices[0]) != shape_vertices_size)) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "executedraw: param error");
        return false;
    }

    glvertexattribpointer(position, pointer_size, gl_float, gl_false, 0, shapevertices);
    glenablevertexattribarray(position);
    glvertexattrib4fv(1, color);
    gldrawarrays(gl_triangle_fan, 0, triangle_fan_size);
    gldisablevertexattribarray(position);

    return true;
}

// 结束绘制操作
bool eglcore::finishdraw()
{
    // 强制刷新缓冲
    glflush();
    glfinish();
    return eglswapbuffers(egldisplay_, eglsurface_);
}

(2) 绘制图形。

void eglcore::draw()
{
    flag_ = false;
    oh_log_print(log_app, log_info, log_print_domain, "eglcore", "draw");
    glint position = preparedraw();
    if (position == position_error) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw get position failed");
        return;
    }

    // 绘制背景
    if (!executedraw(position, background_color, background_rectangle_vertices,
        sizeof(background_rectangle_vertices))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw execute draw background failed");
        return;
    }
    
    // 将五角星分为五个四边形,计算其中一个四边形的四个顶点
    glfloat rotatex = 0;
    glfloat rotatey = fifty_percent * height_;
    glfloat centerx = 0;
    glfloat centery = -rotatey * (m_pi / 180 * 54) * (m_pi / 180 * 18);
    glfloat leftx = -rotatey * (m_pi / 180 * 18);
    glfloat lefty = 0;
    glfloat rightx = rotatey * (m_pi / 180 * 18);
    glfloat righty = 0;

    // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    const glfloat shapevertices[] = {
        centerx / width_, centery / height_,
        leftx / width_, lefty / height_,
        rotatex / width_, rotatey / height_,
        rightx / width_, righty / height_
    };
    
    if (!executedrawstar(position, draw_color, shapevertices, sizeof(shapevertices))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw execute draw star failed");
        return;
    }
    
    glfloat rad = m_pi / 180 * 72;
    for (int i = 0; i < 4; ++i) 
    {
        // 旋转得其他四个四边形的顶点
        rotate2d(centerx, centery, &rotatex, &rotatey,rad);
        rotate2d(centerx, centery, &leftx, &lefty,rad);
        rotate2d(centerx, centery, &rightx, &righty,rad);
        
        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
        const glfloat shapevertices[] = {
                centerx / width_, centery / height_,
                leftx / width_, lefty / height_,
                rotatex / width_, rotatey / height_,
                rightx / width_, righty / height_
            };
        
        // 绘制图形
        if (!executedrawstar(position, draw_color, shapevertices, sizeof(shapevertices))) {
            oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw execute draw star failed");
            return;
        }
    }

    // 结束绘制
    if (!finishdraw()) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw finishdraw failed");
        return;
    }

    flag_ = true;
}

(3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。

void eglcore::changecolor()
{
    if (!flag_) {
        return;
    }
    oh_log_print(log_app, log_info, log_print_domain, "eglcore", "changecolor");
    glint position = preparedraw();
    if (position == position_error) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "changecolor get position failed");
        return;
    }

    // 绘制背景
    if (!executedraw(position, background_color, background_rectangle_vertices,
        sizeof(background_rectangle_vertices))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "changecolor execute draw background failed");
        return;
    }

    // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    glfloat rotatex = 0;
    glfloat rotatey = fifty_percent * height_;
    glfloat centerx = 0;
    glfloat centery = -rotatey * (m_pi / 180 * 54) * (m_pi / 180 * 18);
    glfloat leftx = -rotatey * (m_pi / 180 * 18);
    glfloat lefty = 0;
    glfloat rightx = rotatey * (m_pi / 180 * 18);
    glfloat righty = 0;

    // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    const glfloat shapevertices[] = {
        centerx / width_, centery / height_,
        leftx / width_, lefty / height_,
        rotatex / width_, rotatey / height_,
        rightx / width_, righty / height_
    };
    
    // 使用新的颜色绘制
    if (!executedrawstar2(position, change_color, shapevertices, sizeof(shapevertices))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw execute draw star failed");
        return;
    }

    glfloat rad = m_pi / 180 * 72;
    for (int i = 0; i < 4; ++i)
    {
        // 旋转得其他四个四边形的顶点
        rotate2d(centerx, centery, &rotatex, &rotatey,rad);
        rotate2d(centerx, centery, &leftx, &lefty,rad);
        rotate2d(centerx, centery, &rightx, &righty,rad);
        
        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
        const glfloat shapevertices[] = {
                centerx / width_, centery / height_,
                leftx / width_, lefty / height_,
                rotatex / width_, rotatey / height_,
                rightx / width_, righty / height_
            };

        // 使用新的颜色绘制
        if (!executedrawstar2(position, change_color, shapevertices, sizeof(shapevertices))) {
            oh_log_print(log_app, log_error, log_print_domain, "eglcore", "draw execute draw star failed");
            return;
        }
    }

    // 结束绘制
    if (!finishdraw()) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "changecolor finishdraw failed");
    }
}
  1. 释放相关资源

(1) eglcore类下创建release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。

void eglcore::release()
{
    // 释放surface
    if ((egldisplay_ == nullptr) || (eglsurface_ == nullptr) || (!egldestroysurface(egldisplay_, eglsurface_))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "release egldestroysurface failed");
    }
    // 释放context
    if ((egldisplay_ == nullptr) || (eglcontext_ == nullptr) || (!egldestroycontext(egldisplay_, eglcontext_))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "release egldestroycontext failed");
    }
    // 释放display
    if ((egldisplay_ == nullptr) || (!eglterminate(egldisplay_))) {
        oh_log_print(log_app, log_error, log_print_domain, "eglcore", "release eglterminate failed");
    }
}

(2) pluginrender类添加release()方法,释放eglcore实例及pluginrender实例。

void pluginrender::release(std::string &id)
{
    pluginrender *render = pluginrender::getinstance(id);
    if (render != nullptr) {
        render->eglcore_->release();
        delete render->eglcore_;
        render->eglcore_ = nullptr;
        delete render;
        render = nullptr;
        instance_.erase(instance_.find(id));
    }
}
  1. cmakelists,使用cmake工具链将c++源代码编译成动态链接库文件。
# 设置cmake最小版本
cmake_minimum_required(version 3.4.1)
# 项目名称
project(xcomponent)

set(nativerender_root_path ${cmake_current_source_dir})
add_definitions(-dohos_platform)
# 设置头文件搜索目录
include_directories(
    ${nativerender_root_path}
    ${nativerender_root_path}/include
)
# 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
add_library(nativerender shared
    render/egl_core.cpp
    render/plugin_render.cpp
    manager/plugin_manager.cpp
    napi_init.cpp
)

find_library(
    egl-lib
    egl
)

find_library(
    gles-lib
    glesv3
)

find_library(
    hilog-lib
    hilog_ndk.z
)

find_library(
    libace-lib
    ace_ndk.z
)

find_library(
    libnapi-lib
    ace_napi.z
)

find_library(
    libuv-lib
    uv
)
# 添加构建需要链接的库
target_link_libraries(nativerender public
    ${egl-lib} ${gles-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})

鸿蒙全栈开发全新学习指南

为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(harmonyos next)全栈开发技术的学习路线【包含了大厂app实战项目开发】

本路线共分为四个阶段

第一阶段:鸿蒙初中级开发必备技能

在这里插入图片描述

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/mnxiaona/733gh

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/mnxiaona/733gh

《鸿蒙 (harmony os)开发学习手册》(共计892页)

如何快速入门?

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

开发基础知识:gitee.com/mnxiaona/733gh

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

基于arkts 开发

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

鸿蒙开发面试真题(含参考答案):gitee.com/mnxiaona/733gh

鸿蒙入门教学视频:

美团app实战开发教学:gitee.com/mnxiaona/733gh

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/mnxiaona/733gh

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com