Cocos Creator Android 平臺(tái)監(jiān)聽(tīng)返回按鍵無(wú)效的解決方案

一、返回按鍵處理

根據(jù) 官方文檔 的介紹,我們知道,在 Cocos Creator 中,處理返回按鍵的代碼一般如下:

const { ccclass, property } = cc._decorator;

@ccclass
export default class KeyboardEventHandlerComponent extends cc.Component {
    onEnable() {
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this._onKeyDown, this);
    }

    onDisable() {
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this._onKeyDown, this);
    }

    /**
     * 按鍵按下監(jiān)聽(tīng)
     */
    private _onKeyDown(event: cc.Event.EventKeyboard) {
        switch (event.keyCode) {
            case cc.macro.KEY.back:
                // 處理你的邏輯,如退出游戲:cc.game.end();
                break;
        }
    }
}

正常情況下,上述代碼沒(méi)什么問(wèn)題,但是,在Android平臺(tái)上,在一些特定的場(chǎng)合中,可能會(huì)存在無(wú)法監(jiān)聽(tīng)的情況:

  • 存在 cc.EditBox 的時(shí)候,可能無(wú)法監(jiān)聽(tīng)上述事件
  • 顯示 cc.WebView 并且點(diǎn)擊過(guò) cc.WebView 時(shí),此時(shí)按下返回按鍵,將通過(guò)上述方法監(jiān)聽(tīng)返回事件
  • ...

因此,我們還需要做一些處理才可以通過(guò)上面的方法監(jiān)聽(tīng)到返回按鍵的回調(diào)

更新于 2022年 4月27日
有時(shí)候什么都不做,也可能存在監(jiān)聽(tīng)不到按鍵事件的情況。
解決方案:

參考評(píng)論,需要在 cocos2dxGLSurfaceViewinitView 中調(diào)用 requestFocus(); 事件才能收到,否則收不到

二、Android 事件分發(fā)機(jī)制

剛剛提及到的無(wú)法監(jiān)聽(tīng)問(wèn)題,其根本原因在于Android 的事件分發(fā)機(jī)制。因此我們有必要了解一下 Android 的事件分發(fā)機(jī)制

這里我們簡(jiǎn)單了解一下 Android 的事件分發(fā)機(jī)制。大概如下:

ViewRootImpl->DecorView->Activity->ViewGroup->View

以按鍵事件為例:

  1. 如果事件沒(méi)有被攔截消費(fèi)掉,那么事件會(huì)一層一層回調(diào)下去。
  2. 特別地,在最后一層的 View 中,View 還需要獲取到焦點(diǎn)時(shí),才會(huì)回調(diào)其對(duì)應(yīng)的按鍵事件。

在 Cocos Creator 中,Android 上的按鍵事件監(jiān)聽(tīng)實(shí)現(xiàn)全部都在 View 中進(jìn)行處理,Activity 并沒(méi)有進(jìn)行 onKeyDown onKeyUp 的重寫處理,因此我們需要關(guān)注 View 層面的事件處理

而在 Cocos Creator 中,Android 平臺(tái)上大概封裝了4個(gè)原生View

NativeView

其中,每個(gè) View 的對(duì)應(yīng)的CC組件封裝關(guān)系如下:

  • Cocos2dxEditBox -> cc.EditBox
  • Cocos2dxGLSurfaceView -> 渲染游戲
  • Cocos2dxVideoView -> cc.VideoPlayer
  • Cocos2dxWebView -> cc.WebView

在按鍵事件處理上,以 Cocos2dxGLSurfaceView 為例:

Cocos2dxGLSurfaceView

可以看到其重寫了 View 的 onKeyDownonKeyUp 方法,經(jīng)過(guò)一系列的調(diào)用,最終使得用戶在按下和釋放按鍵時(shí),能回調(diào) Cocos 的方法并最終回調(diào)到我們一開(kāi)始在游戲中注冊(cè)的按鍵監(jiān)聽(tīng)方法

cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this._onKeyDown, this);

OK,基本科普完畢,后面將介紹一些特殊情況處理

2.1 cc.EditBox 默認(rèn)獲取焦點(diǎn),沒(méi)有回調(diào)按鍵事件

問(wèn)題描述

以下面場(chǎng)景為例(可以理解為手機(jī)登錄界面),存在一個(gè) cc.EditBox 。在什么都沒(méi)做的時(shí)候,首次進(jìn)入這個(gè)場(chǎng)景,點(diǎn)擊返回按鍵,就會(huì)直接退出游戲,而不是回調(diào)我們注冊(cè)的 cc.SystemEvent.EventType.KEY_DOWN

Demo

問(wèn)題分析

在第二章節(jié)中,我們知道,cc.EditBox 在 Android 平臺(tái)上的實(shí)現(xiàn)代碼在 Cocos2dxEditBox 中。

查閱 Cocos2dxEditBox ,我們可以知道,在其顯示的時(shí)候,也就是 cc.EditBox 顯示的時(shí)候,默認(rèn)是獲取焦點(diǎn)的

Cocos2dxEditBox

在結(jié)合第二章節(jié)中,我們提及到的另外一個(gè)知識(shí)點(diǎn)

在最后一層的 View 中,View 還需要獲取到焦點(diǎn)時(shí),才會(huì)回調(diào)其對(duì)應(yīng)的按鍵事件

因此我們需要在查閱一下 Cocos2dxEditBox 是否有重寫 onKeyDown 和 onKeyUp 方法。經(jīng)過(guò)查閱,發(fā)現(xiàn)是沒(méi)有進(jìn)行重寫的。

那么問(wèn)題就很明顯了

cc.EditBox 在默認(rèn)顯示(沒(méi)做處理)的時(shí)候,默認(rèn)獲取到焦點(diǎn)。此時(shí),又因?yàn)闆](méi)有特別處理返回按鍵,導(dǎo)致按下返回按鍵時(shí),就跳過(guò)事件,即沒(méi)消費(fèi)(Android的按鍵)事件,事件在后續(xù)的傳遞中,因?yàn)闆](méi)有其他地方做特殊處理,最終就直接退出應(yīng)用,而不是回調(diào)我們注冊(cè)的 cc.SystemEvent.EventType.KEY_DOWN

當(dāng)然,在 cc.EditBox 獲取到焦點(diǎn)時(shí),我們點(diǎn)擊他并嘗試輸入點(diǎn)什么,然后結(jié)束輸入時(shí),焦點(diǎn)就會(huì)重新交會(huì)給 Cocos2dxGLSurfaceView ,后面的返回按鍵我們還是能監(jiān)聽(tīng)到,因?yàn)榻裹c(diǎn)已經(jīng)交回給 Cocos2dxGLSurfaceView

Cocos2dxGLSurfaceView

解決方案

因?yàn)閱?wèn)題就在于 cc.EditBox 默認(rèn)顯示時(shí)就獲取到焦點(diǎn),導(dǎo)致無(wú)法監(jiān)聽(tīng)按鍵事件。所以其實(shí)解決方案也很簡(jiǎn)單。

在 onLoad 的時(shí)候,默認(rèn)不要讓 cc.EditBox 獲取到焦點(diǎn)即可,那么這個(gè)時(shí)候,點(diǎn)擊返回按鍵,就會(huì)回調(diào)我們注冊(cè)的cc.SystemEvent.EventType.KEY_DOWN

const { ccclass, property } = cc._decorator;

@ccclass
export default class SystemEventComponent extends cc.Component {
    @property(cc.EditBox)
    editBox: cc.EditBox = null;

    onLoad() {
        // 讓 Editbox 失去焦點(diǎn)
        this.editBox.blur();
    }
}

2.2 cc.WebView 獲取焦點(diǎn)時(shí),沒(méi)有回調(diào)按鍵事件

問(wèn)題描述

在展示 webview 的時(shí)候,webview獲取到焦點(diǎn)后,點(diǎn)擊返回按鍵,將直接退出應(yīng)用,而不是回調(diào)我們注冊(cè)的 cc.SystemEvent.EventType.KEY_DOWN

問(wèn)題分析

問(wèn)題其實(shí)和上一小節(jié)很類似,總結(jié)為

Cocos2dxWebView 沒(méi)有重寫按鍵相關(guān)監(jiān)聽(tīng)。在 Cocos2dxWebView 獲取到焦點(diǎn)的時(shí)候,按鍵事件沒(méi)有被 Webview 消費(fèi),事件在后續(xù)的傳遞中,因?yàn)闆](méi)有其他地方做特殊處理,最終就直接退出應(yīng)用,而不是回調(diào)我們注冊(cè)的 cc.SystemEvent.EventType.KEY_DOWN

解決方案

修改 Cocos2dxWebView 加入按鍵監(jiān)聽(tīng)處理,如下面示例最終效果為

在 webview 獲取焦點(diǎn)時(shí),點(diǎn)擊返回按鍵,回調(diào)我們的注冊(cè)的事件 cc.SystemEvent.EventType.KEY_DOWN

Cocos2dxWebView
this.setOnKeyListener(new OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            Cocos2dxGLSurfaceView.getInstance().onKeyDown(keyCode, event);
            return true;
        }
        if (event.getAction() == KeyEvent.ACTION_UP) {
            Cocos2dxGLSurfaceView.getInstance().onKeyUp(keyCode, event);
            return true;
        }
        return false;
    }
});

三、總結(jié)

  • cc.VideoPlayer 我們沒(méi)有做分析處理,因?yàn)檫€沒(méi)有怎么用過(guò),但是如果遇到類似問(wèn)題,應(yīng)該可以采用相同方法處理
  • 上面介紹中,我們著重針對(duì) onKeyDown 進(jìn)行處理并且是針對(duì)返回按鍵進(jìn)行介紹,onKeyUp我們并沒(méi)有細(xì)講,不過(guò)原理相同,大家可以自行處理

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容