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

news2024/12/24 3:18:38

场景介绍

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

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

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

相关文章

理性决策的艺术:从购房到择偶的数学智慧;37% 规则,做出最佳决策的秘诀;用数学模型解决人生难题

在面对人生重大决策时&#xff0c;如购房或择偶&#xff0c;我们常常感到迷茫和困惑。然而&#xff0c;如果我们能够将这些看似复杂的问题简化为数学模型&#xff0c;我们就能以更加理性和系统的方式做出决策。 37%规则 1950年代&#xff0c;当时几位数学家开始研究这样一个问…

钉钉开放AI生态战略的真正价值到底是什么?很多人都没看懂

来源&#xff1a; 首席数智官 hello 大家好&#xff0c;我们是数字化领军者都在看的首席数智官。 关注我&#xff0c;每天给你讲一个商业案例。 今天我们要给你讲的是&#xff1a;钉钉开放AI大模型生态的战略意义到底是什么&#xff1f; 「谁先赢得苹果&#xff0c;谁就赢得…

C++实现简化版Qt的QObject(3):增加父子关系、属性系统

前几天写了文章&#xff1a; C实现一个简单的Qt信号槽机制 C实现简化版Qt信号槽机制&#xff08;2&#xff09;&#xff1a;增加内存安全保障 之后感觉还不够过瘾&#xff0c;Qt中的QObject体系里还有不少功能特性没有实现。为了提高QObject的还原度&#xff0c;今天我们将父子…

欢迎回家!揭秘“嫦娥六号”背后的守望者

6月25日&#xff0c;嫦娥六号返回器携带来自月背的月球样品安全着陆在内蒙古四子王旗预定区域。这是时隔3年多后&#xff0c;中国探月工程的又一关键节点任务&#xff0c;也是时隔5年多后&#xff0c;嫦娥探测器再去月球背面。 在此次任务中&#xff0c;同元软控数字伴飞团队为…

C++使用Poco库封装一个HTTP客户端类

0x00 前言 我们在使用HTTP协议获取接口数据时&#xff0c;通常需要在Header和Query中添加参数&#xff0c;还有一种就是在Body中追加XML或者JSON格式的数据。本文主要讲述使用Poco库提交HTTP Post请求的Body中附加XML格式的数据&#xff0c;JSON格式的数据类似。 0x01 HttpCl…

Zuul介绍

Zuul 是 Netflix 开源的一个云平台网络层代理&#xff0c;它主要用于路由、负载均衡、中间件通信和动态路由。Zuul 本质上是一个基于 JVM 的网关&#xff0c;它提供了以下功能&#xff1a; 1.路由&#xff1a;Zuul 允许客户端和服务器之间的所有入站和出站请求通过一个中心化的…

Hadoop页面报错Permission denied: user=dr.who, access....

1、临时解决 hdfs dfs -chmod -R 777 /这种方法&#xff0c;存在一个不足&#xff0c;就是后面重新创建的文件夹&#xff0c;页面进行删除的时候&#xff0c;依然报这个错。 但是&#xff0c;对于应付紧急客户需求&#xff0c;可以临时用一下。 2、永久解决 查看页面的Owner…

超声波清洗机怎么选?极力推荐四款口碑大牌超声波清洗机

相信大家都知道超声波清洗机&#xff0c;每次眼镜脏的时候&#xff0c;去眼镜店里让老板帮忙清洗&#xff0c;她们用的就是超声波清洗机&#xff0c;通过超声波的原理深入物品深处清洁&#xff0c;清洁效果非常好。相对手洗的方式&#xff0c;超声波清洗机能够保护镜片在清洗过…

SpringCloud_Ribbon负载均衡

概述 SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。 源码 LoadBalancerInterceptor 其中含有intercept方法&#xff0c;拦截用户的HttpRequest请求&#xff1a; request.getURI() 获取请求uri&#xff0c;即http://userservice/use…

【哈哈大一上学的全忘了,重开!!】STM32从零入门物联网开发

本笔记资料来源 &#xff1a;STM32物联网入门30步&#xff1d;单片机物联网入门教程 WIFI连接阿里云物联网CubeMXHAL库蓝牙ESP8266杜洋主讲_哔哩哔哩_bilibili IOT&#xff1a;Internet of things 学习目标&#xff1a; 1.掌握洋桃IoT开发板的各功能以及驱动与基本应用 2.掌…

Python容器 之 字符串--字符串的常用操作方法

1.字符串查找方法 find() 说明&#xff1a;被查找字符是否存在于当前字符串中。 格式&#xff1a;字符串.find(被查找字符) 结果&#xff1a;如果存在则返回第一次出现 被查找字符位置的下标 如果不存在则返回 -1 需求&#xff1a; 1. 现有字符串数据: 我是中国人 2. 请设计程序…

vue根据文字长短展示跑马灯效果

介绍 为大家介绍一个我编写的vue组件 auto-marquee &#xff0c;他可以根据要展示文本是否超出展示区域&#xff0c;来判断是否使用跑马灯效果&#xff0c;效果图如下所示 假设要展示区域的宽度为500px&#xff0c;当要展示文本的长度小于500px时&#xff0c;只会展示文本&…

mysql5.7.30忘记root密码

windows系统安装了mysql5.7.30&#xff0c;在使用navicat链接mysql时候&#xff0c;提示 如何解决&#xff1a; 打开任务管理器的服务&#xff0c;查看有没有MYSQL服务。 如果没有&#xff0c;则按照下面的csdn博客进行操作。 https://blog.csdn.net/clj198606061111/article…

PostgreSQL的学习心得和知识总结(一百四十七)|深入理解PostgreSQL数据库之transaction chain的使用和实现

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

Firewalld 概述

1.firewalld简介 firewalld的作用是为包过滤机制提供匹配规则(或称为策略),通过各种不同的规则&#xff0c;告诉 netfiter对来自指定源、前往指定目的或具有某些协议特征的数据包采取何种处理方式。 为了更加方便地组织和管理防火墙&#xff0c;firewalld 提供了支持网络区域…

【笔记】手工部署之linux中开放已安装的mysql与tomcat端口

在需要打包的springboot项目中输入mvn clean package 在target下面获得jar包 进入linux中你想要该jar包存在的位置 将jar包上传至linux中 此时在浏览器中输入linux的ip地址&#xff1a;端口号/mapping路径为404 故&#xff1a; 在linux中另开一个标签页 检查mysql和tomcat已…

JMH319【亲测整理】2017剑侠情缘2剑网2汉化版+网游VM单机版+修复纹饰翅膀+内置GM命令无限道具+一键端视频安装教学

资源介绍&#xff1a; 这一套新端早就在手上 一直没分享出来 既然大家放出来了 我也就发出来大家研究吧 目前在改另外一套 是否需要虚拟机&#xff1a;是 文件大小&#xff1a;压缩包约7G 支持系统&#xff1a;win7、win10、win11 硬件需求&#xff1a;运行内存12G …

遇到多语言跨境电商系统源码问题?这里有解决方案!

从手机到电脑&#xff0c;从线下到线上&#xff0c;如今&#xff0c;跨境电商正在打破地域界限&#xff0c;成为全球贸易的新引擎。在这个全球化的背景下&#xff0c;跨境电商平台的运营也面临着一系列的挑战&#xff0c;其中之一就是多语言问题。如果你遇到了多语言跨境电商系…

c进阶篇(三):字符串函数

1.strlen: strlen - C Reference strlen 函数是一个标准库函数&#xff0c;用于计算以 null 结尾的字符串的长度&#xff0c;也就是字符串中实际字符的数量&#xff0c;不包括最后的 null 终止符 \0。它定义在 <string.h> 头文件中。 函数原型:size_t strlen(const ch…

PyCharm 著名编程利器下载安装:Python开发者的必备神器!

PyCharm&#xff0c;它不仅拥有卓越的性能&#xff0c;更以其丰富多样的功能和工具&#xff0c;让开发者在 Python 项目的开发中事半功倍&#xff0c;无论是 Web 开发、科学计算&#xff0c;还是数据分析等领域&#xff0c;都能轻松应对。 在代码编辑方面&#xff0c;PyCharm 的…