React Native跨平台鸿蒙开发高级应用原理:适配层事件分发逻辑(一)
适配层事件分发逻辑
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;
});
}
}

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


所有评论(0)