打开应用无法立即获得焦点问题分析报告
1 关键字
焦点;应用;Tab;
2 问题描述
环境:OpenHarmony 3.2
问题现象:打开应用无法立即获得焦点。需点击键盘Tab按键才可获得焦点。
3 问题原因
3.1 正常机制
打开应用默认元素获得焦点。
3.2 异常机制
打开应用无法立即获得焦点,点击键盘Tab按键默认元素获得焦点。
4 解决方案
更改获得焦点默认逻辑,使得应用进入默认获得焦点。
修改文件foundation/arkui/ace_engine/frameworks/core/pipeline/pipeline_context.h
中isTabKeyPressed_
属性默认值为true
。
bool isTabKeyPressed_ = true;
5 定位过程
-
在设置默认焦点和焦点控制时,发现进入应用无法控制应用焦点。点击键盘
Tab
按键后,默认焦点生效,焦点也可进行控制。 -
由于键盘输入属于多模输入模块,从多模输入模块中查找
Tab
按键。查找
foundation/multimodalinput/input/frameworks/proxy/events/src/key_event.cpp
文件。const int32_t KeyEvent::KEYCODE_TAB = 2049;
-
查找
KeyEvent::KEYCODE_TAB
是否经过特别处理。发现未经过特别处理,继续查找多模模块分发过程中是否对
TAB
按键进行特别处理。 -
多模模块通过
foundation/multimodalinput/input/frameworks/proxy/event_handler/src/input_manager_impl.cpp
文件,将key_event
事件分发到窗口管理子系统。void InputManagerImpl::OnKeyEvent(std::shared_ptr<KeyEvent> keyEvent) { CHK_PID_AND_TID(); CHKPV(keyEvent); CHKPV(eventHandler_); CHKPV(consumer_); std::lock_guard<std::mutex> guard(mtx_); BytraceAdapter::StartBytrace(keyEvent, BytraceAdapter::TRACE_STOP, BytraceAdapter::KEY_DISPATCH_EVENT); if (!MMIEventHandler::PostTask(eventHandler_, std::bind(&InputManagerImpl::OnKeyEventTask, this, consumer_, keyEvent))) { MMI_HILOGE("Post task failed"); } MMI_HILOGD("Key event keyCode:%{public}d", keyEvent->GetKeyCode()); } void InputManagerImpl::OnKeyEventTask(std::shared_ptr<IInputEventConsumer> consumer, std::shared_ptr<KeyEvent> keyEvent) { CHK_PID_AND_TID(); CHKPV(consumer); consumer->OnInputEvent(keyEvent); MMI_HILOGD("Key event callback keyCode:%{public}d", keyEvent->GetKeyCode()); }
-
继续跟踪,
foundation/window/window_manager/wm/src/window_input_channel.cpp
接收到事件,未做特殊处理。void WindowInputChannel::HandleKeyEvent(std::shared_ptr<MMI::KeyEvent>& keyEvent) { if (keyEvent == nullptr) { WLOGFE("keyEvent is nullptr"); return; } WLOGFI("Receive key event, windowId: %{public}u, keyCode: %{public}d", window_->GetWindowId(), keyEvent->GetKeyCode()); if (window_->GetType() == WindowType::WINDOW_TYPE_DIALOG) { if (keyEvent->GetAgentWindowId() != keyEvent->GetTargetWindowId()) { window_->NotifyTouchDialogTarget(); keyEvent->MarkProcessed(); return; } } bool inputMethodHasProcessed = false; #ifdef IMF_ENABLE bool isKeyboardEvent = IsKeyboardEvent(keyEvent); if (isKeyboardEvent) { WLOGFI("dispatch keyEvent to input method"); inputMethodHasProcessed = MiscServices::InputMethodController::GetInstance()->dispatchKeyEvent(keyEvent); } #endif // IMF_ENABLE if (!inputMethodHasProcessed) { WLOGFI("dispatch keyEvent to ACE"); window_->ConsumeKeyEvent(keyEvent); } }
-
继续跟踪,
foundation/window/window_manager/wm/src/window_impl.cpp
接收到事件,未做特殊处理,并将事件分发到ArkUI中。void WindowImpl::ConsumeKeyEvent(std::shared_ptr<MMI::KeyEvent>& keyEvent) { int32_t keyCode = keyEvent->GetKeyCode(); int32_t keyAction = keyEvent->GetKeyAction(); WLOGFI("KeyCode: %{public}d, action: %{public}d", keyCode, keyAction); if (keyCode == MMI::KeyEvent::KEYCODE_BACK && keyAction == MMI::KeyEvent::KEY_ACTION_UP) { HandleBackKeyPressedEvent(keyEvent); } else { std::shared_ptr<IInputEventConsumer> inputEventConsumer; { std::lock_guard<std::recursive_mutex> lock(mutex_); inputEventConsumer = inputEventConsumer_; } if (inputEventConsumer != nullptr) { WLOGFI("Transfer key event to inputEventConsumer"); (void)inputEventConsumer->OnInputEvent(keyEvent); } else if (uiContent_ != nullptr) { WLOGFI("Transfer key event to uiContent"); (void)uiContent_->ProcessKeyEvent(keyEvent); } else { WLOGFE("There is no key event consumer"); } } }
-
跟踪ArkUI对事件的处理,发现
foundation/arkui/ace_engine/frameworks/core/pipeline/pipeline_context.cpp
中,设置当第一次点击TAB
时会把isTabKeyPressed_
属性设置为true
。bool PipelineContext::OnKeyEvent(const KeyEvent& event) { CHECK_RUN_ON(UI); if (!rootElement_) { LOGE("the root element is nullptr"); EventReport::SendAppStartException(AppStartExcepType::PIPELINE_CONTEXT_ERR); return false; } if (!isKeyEvent_ && SystemProperties::GetDeviceType() == DeviceType::PHONE) { if (KeyCode::KEY_DPAD_UP <= event.code && event.code <= KeyCode::KEY_DPAD_RIGHT) { if (event.action == KeyAction::UP) { SetIsKeyEvent(true); } } else if (event.code == KeyCode::KEY_ENTER) { if (event.action == KeyAction::CLICK) { SetIsKeyEvent(true); } } } rootElement_->HandleSpecifiedKey(event); SetShortcutKey(event); pressedKeyCodes = event.pressedCodes; isKeyCtrlPressed_ = !pressedKeyCodes.empty() && (pressedKeyCodes.back() == KeyCode::KEY_CTRL_LEFT || pressedKeyCodes.back() == KeyCode::KEY_CTRL_RIGHT); if ((event.code == KeyCode::KEY_CTRL_LEFT || event.code == KeyCode::KEY_CTRL_RIGHT) && event.action == KeyAction::UP) { if (isOnScrollZoomEvent_) { zoomEventA_.type = TouchType::UP; zoomEventB_.type = TouchType::UP; LOGI("Send TouchEventA(%{public}f, %{public}f, %{public}zu)", zoomEventA_.x, zoomEventA_.y, zoomEventA_.type); OnTouchEvent(zoomEventA_); LOGI("Send TouchEventB(%{public}f, %{public}f, %{public}zu)", zoomEventB_.x, zoomEventB_.y, zoomEventB_.type); OnTouchEvent(zoomEventB_); isOnScrollZoomEvent_ = false; } } if (event.code == KeyCode::KEY_TAB && event.action == KeyAction::DOWN && !isTabKeyPressed_) { isTabKeyPressed_ = true; } if (!eventManager_->DispatchTabIndexEvent(event, rootElement_, GetLastPage())) { return eventManager_->DispatchKeyEvent(event, rootElement_); } return true; }
-
查找
isTabKeyPressed_
调用位置,在foundation/arkui/ace_engine/frameworks/core/components/button/button_element.cpp
文件中,发现获得焦点需要判断isTabKeyPressed_
属性为true
。void ButtonElement::OnFocus() { if (!button_) { return; } button_->HandleFocusEvent(true); button_->PlayFocusAnimation(true); auto context = context_.Upgrade(); if (context && context->GetIsTabKeyPressed()) { button_->ChangeStatus(RenderStatus::FOCUS); } }
6 知识分享
焦点控制时,KeyEvent 仅支持上、下、左、右和Tab按键进行焦点选择。
switch (keyEvent.code) {
case KeyCode::TV_CONTROL_UP:
LOGI("Node: %{public}s request next focus by Key-'UP'", GetFrameName().c_str());
return RequestNextFocus(true, true, GetRect());
case KeyCode::TV_CONTROL_DOWN:
LOGI("Node: %{public}s request next focus by Key-'DOWN'", GetFrameName().c_str());
return RequestNextFocus(true, false, GetRect());
case KeyCode::TV_CONTROL_LEFT:
LOGI("Node: %{public}s request next focus by Key-'LEFT'", GetFrameName().c_str());
return RequestNextFocus(false, !AceApplicationInfo::GetInstance().IsRightToLeft(), GetRect());
case KeyCode::TV_CONTROL_RIGHT:
LOGI("Node: %{public}s request next focus by Key-'RIGHT'", GetFrameName().c_str());
return RequestNextFocus(false, AceApplicationInfo::GetInstance().IsRightToLeft(), GetRect());
case KeyCode::KEY_TAB: {
auto context = NG::PipelineContext::GetCurrentContext();
bool ret = false;
if (keyEvent.pressedCodes.size() == 1) {
LOGI("Node: %{public}s request next focus by Key-'TAB'", GetFrameName().c_str());
ret = RequestNextFocus(false, false, GetRect()) || RequestNextFocus(true, false, GetRect());
} else if (keyEvent.IsShiftWith(KeyCode::KEY_TAB)) {
LOGI("Node: %{public}s request next focus by Key-'SHIFT-TAB'", GetFrameName().c_str());
ret = RequestNextFocus(false, true, GetRect()) || RequestNextFocus(true, true, GetRect());
}
return ret;
}
default:
return false;
}
更多推荐
所有评论(0)