适配层事件分发逻辑

1.适配层事件的注册

当手势触碰屏幕后会命中相应的结点,通过回调发送对应事件,但是需要注册事件,如一个 Stack 节点注册了 NODE_ON_CLICK 事件。

StackNode::StackNode()
:ArkUINode(NativeNodeAPi::getInstance()->createNode(ArkUI_NodeType::ARKUI_NODE_STACK)),
    m_stackNodeDelegate(nullptr)
    {
        maybeThrow(NativeNodeApi::getInstance()->registerNodeEvent(m_nodeHandle,NODE_ON_CLICK,0,this));
        maybeThrow(NativeNodeApi::getInstance()->registerNodeEvent(m_nodeHandle,NODE_ON_HOVER,0,this));
    }

SurfaceTouchEventHandler 注册了 NODE_TOUCH_EVENT 事件。

SurfaceTouchEventHandler(
    ComponentInstance::Shared rootView,
    ArkTSMessageHub::Shared arkTSMessageHub,int rnInstanceId):
    ArkTSMessageHub::Observer(arkTSMessageHub),
    m_rootView(std::move(rootView)),
    m_rnInstanceId(rnInstanceId)
    {
        ArkUINodeRegistry::getInstance().registerTouchHandler(
            &m_rootView->getLocalRootArkUINode(),this);
            NativeNodeApi::getInstance()->registerNodeEvent(
                m_rootView->getLocalRootArkUINode().getArkUINodeHandle(),
                NODE_TOUCH_EVENT,
                NODE_TOUCH_EVENT,
                this);
    }
2.适配层事件的接收

ArkUINodeRegistry 的构造中注册了一个回调,当注册了事件的节点被命中后,该事件通过回调传递处理。

ArkUINodeRegistry::ArkUINodeRegistry(ArkTSBridge::Shared arkTSBridge):m_arkTSBridge(std::move(arkTSBridge))
{
    NativeNodeApi::getInstance()->registerNodeEventReceiver(
        [](ArkUI_NodeEvent* event){
            ArkUINodeRegistry::getInstance().receiveEvent(event)});
}
3.适配层事件的处理

回调传递的参数 event 通过 OH_ArkUI_NodeEvent_GetEventType 获取事件类型,通过 OH_ArkUI_NodeEvent_GetNodeHandle获取触发该事件的结点指针。

auto eventType = OHArkUI_NodeEvent_GetEventType(event);
auto node = OH_ArkUI_NodeEvent_GetNodeHandle(event);

首先判断事件类型是否为 Touch 事件,如果是,就从一个存储了所有 TouchEventHandler 的 Map 中通过结点指针作为 key 去查找对应的 TouchEventHandler,如果没找到,这次 Touch 事件不处理。

if(eventType == ArkUI_NodeEventType::NODE_TOUCH_EVENT)
{
    auto it = m_touchHandlerByNodeHandle.find(node);
    if(it == m_touchHandlerByNodeHandle.end())
    {
        return;
    }
}

如果找到了对应的 TouchEventHandler,通过 OH_ArkUI_NodeEvent_GetInputEvent 获取输入事件指针,若输入事件指针不为空,通过 OH_ArkUI_UIInputEvent_GetType 判断输入事件指针的类型是否为 Touch 事件,如果不是,这次 Touch 事件不处理。

auto inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
if(inputEvent == nullptr || OH_ArkUI_UIInputEvent_GetType(inputEvent) != ArkUI_UIInputEvent_Type::ARKUI_UIINPUTEVENT_TYPE_TOUCH)
{
    return;
}

如果上述两个条件都满足,就通过 TouchEventHandler 去处理 Touch 事件。

it->second->onTouchEvent(inputEvent);

如果事件类型不为 Touch 事件,就从一个存储了所有 ArkUINode 的 Map 中通结点指针作为 key 去查找对应的 ArkUINode,若未找到,这次事件不处理。

auto it = m_nodeByHandle.find(node);
if(it == m_nodeByHandle.end())
{
    return;
}

如果找了对应的 ArkUINode,通过 OH_ArkUI_NodeEvent_GetNodeComponentEvent 获取组件事件指针,该指针的 data 字段保留了 arkUI 传递过来的参数,并通过 ArkUINode 处理该事件。

auto commponentEvent = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
if(commponentEvent != nullptr)
{
    it->second->onNodeEvent(eventType,compenentEvent->data);
    return;
}
4.Touch事件的传递给JS侧

上文中写明 TouchEventHandler 对 Touch 事件进行处理,以 ArkUISurface 举例,ArkUISurface 有一个继承了 TouchEventHandler 的成员变量,这个成员变量通过 dispatchTouchEvent 处理这次 Touch 事件。

void onTouchEvent(ArkUI_UIInputEvent* event)override
{
    m_touchEventDispatcher.dispatchTouchEvent(event,m_rootView);
}

对于 Touch 事件首先通过 Touch 的位置等因素,获取对应 touchTarget (每个componentInstance 就是一个 touchTarget,下图的名字是 eventTarget)。

class ComponentInstance:public TouchTarget,public std::enable_shared_from_this<ComponentInstance>
for(auto const& targetTouches:touchByTargetId)
{
    auto it = m_touchTargetByTouchId.find(targetTouches.second.begin()->identifier);
    if(it == m_touchTargetByTouchId.end())
    {
        continue;
    }
    auto eventTarget = it->second.lock();
    if(eventTarget == nullptr)
    {
        m_touchTargetByTouchId.erase(it);
        continue;
    }
}

然后通过 componentInstance 保存的 m_eventEmitter 发送对应的事件给 js 侧,从而触发页面的刷新等操作。
Touch事件有以下四种类型:

  • UI_TOUCH_EVENT_ACTION_DOWN
  • UI_TOUCH_EVENT_ACTION_MOVE
  • UI_TOUCH_EVENT_ACTION_UP
  • UI_TOUCH_EVENT_ACTION_CANCEL
switch(action)
{
    case UI_TOUCH_EVENT_ACTION_DOWN:
    eventTarget->getTouchEventEmitter()->onTouchStart(touchEvent);
    break;
    case UI_TOUCH_EVENT_ACTION_MOVE:
    eventTarget->getTouchEventEmitter()->onTouchMove(touchEvent);
    break;
    case UI_TOUCH_EVENT_ACTION_UP:
    eventTarget->getTouchEventEmitter()->onTouchEnd(touchEvent);
    break;
    case UI_TOUCH_EVENT_ACTION_CANCEL:
    default:
    eventTarget->getTouchEventEmitter()->onTouchCancel(touchEvent);
    break;
}
5、非Touch事件的传递给js侧

上文中写明,非 Touch 事件由 ArkUINode 处理,对于每个继承了 ArkUINode 的类,重载了 onNodeEvent 方法,以 StackNode 举例,说明RN适配层是如何区分 Click 事件和 Touch 事件。前文说明,StackNode 注册了 Click 事件,所以通过回调,会走到 StackNode 的 onNodeEvent 部分,这里会先判断这个事件类型,这里是 NODE_ON_CLICK 类型,符合要求,但是对于第二个条件 eventArgs[3].i32 (即上文描述的arkUI传递过来的参数),如果是触屏手机,其值为2不满足 eventArgs[3].i32 != 2 的条件。

void StackNode::onNodeEvent(ArkUI_NodeEventType eventType,EventArgs& eventArgs)
{
    if(eventType == ArkUI_NodeEventType::NODE_ON_CLICK && eventArgs[3].i32 != 2)
    {
        onClick();
    }
    if(eventType == ArkUI_NodeEventType::NODE_ON_HOVER)
    {
        if(m_stackNodeDelegate != nullptr)
        {
            if(eventArgs[0].i32)
            {
                m_stackNodeDelegate->onHoverIn();
            }else
            {
                m_stackNodeDelegate->onHoverOut();
            }
        }
    }
}

所以此时实际上不会触发 Click 的事件,因此 Touch 事件和 Click 事件不会冲突。如果触发了 Click 事件,StackNode 会通过代理 StackNodeDelegate 发送事件。

void StackNode::onClick()
{
    if(m_stackNodeDelegate != nullptr)
    {
        m_stackNodeDelegate->onClick();
    }
}

其中 ViewComponentInstance 继承了 StackNodeDelegate,所以实际上走的是 ViewComponentInstance 的 onClick 函数。

namespace rnoh
{
    class ViewComponentInstance
    :public CppComponentInstance<facebook::react::ViewShardowNode>,public StackNodeDelegate
    {
    }
}

这个函数通过 ViewComponentInstance 的 m_eventEmitter 发送事件给 JS,从而触发页面的刷新。

void ViewComponentInstance::onClick()
{
    if(m_eventEmitter != nullptr)
    {
        m_eventEmitter->dispatchEvent("click",[=](facebook:jsi::Runtime& runtime)
        {auto payload = facebook::jsi::Object(runtime);
                return payload;
        });
    }
}

在这里插入图片描述

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐