Android中外接鍵盤(pán)的檢測(cè)的實(shí)現(xiàn)
今天來(lái)了一個(gè)問(wèn)題:軟鍵盤(pán)無(wú)法彈出。分析后是因?yàn)橄到y(tǒng)判斷當(dāng)前有外接硬鍵盤(pán),就會(huì)隱藏軟鍵盤(pán)。但實(shí)際情況并不是這么簡(jiǎn)單,該問(wèn)題只有在特定條件下偶現(xiàn),具體分析過(guò)程就不說(shuō)了,就是軟硬鍵盤(pán)支持上的邏輯問(wèn)題。借著這個(gè)機(jī)會(huì)整理一下鍵盤(pán)檢測(cè)的過(guò)程。
Configuration
Android系統(tǒng)中通過(guò)讀取Configuration中keyboard的值來(lái)判斷是否存在外接鍵盤(pán)。Configuration中關(guān)于鍵盤(pán)類型的定義如下,
public static final int KEYBOARD_UNDEFINED = 0; // 未定義的鍵盤(pán) public static final int KEYBOARD_NOKEYS = 1; // 無(wú)鍵鍵盤(pán),沒(méi)有外接鍵盤(pán)時(shí)為該類型 public static final int KEYBOARD_QWERTY = 2; // 標(biāo)準(zhǔn)外接鍵盤(pán) public static final int KEYBOARD_12KEY = 3; // 12鍵小鍵盤(pán)
在最常見(jiàn)的情況下,外接鍵盤(pán)未連接時(shí)keyboard的值為KEYBOARD_NOKEYS,當(dāng)檢測(cè)到鍵盤(pán)連接后會(huì)將keyboard的值更新為KEYBOARD_QWERTY 。應(yīng)用就可以根據(jù)keyboard的值來(lái)判斷是否存在外接鍵盤(pán),InputMethodService.java中有類似的判斷代碼。
// 軟件盤(pán)是否可以顯示 public boolean onEvaluateInputViewShown() { Configuration config = getResources().getConfiguration(); return config.keyboard == Configuration.KEYBOARD_NOKEYS || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; }
現(xiàn)在的問(wèn)題就轉(zhuǎn)向Configuration的keyboard是如何更新的。在WindowManagerService.java中,應(yīng)用啟動(dòng)時(shí)會(huì)更新Configuration,相關(guān)代碼如下。
boolean computeScreenConfigurationLocked(Configuration config) { ...... if (config != null) { // Update the configuration based on available input devices, lid switch, // and platform configuration. config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; // 默認(rèn)值為KEYBOARD_NOKEYS config.keyboard = Configuration.KEYBOARD_NOKEYS; config.navigation = Configuration.NAVIGATION_NONAV; int keyboardPresence = 0; int navigationPresence = 0; final InputDevice[] devices = mInputManager.getInputDevices(); final int len = devices.length; // 遍歷輸入設(shè)備 for (int i = 0; i < len; i++) { InputDevice device = devices[i]; // 如果不是虛擬輸入設(shè)備,會(huì)根據(jù)輸入設(shè)備的flags來(lái)更新Configuration if (!device.isVirtual()) { ...... // 如果輸入設(shè)備的鍵盤(pán)類型為KEYBOARD_TYPE_ALPHABETIC,則將keyboard設(shè)置為KEYBOARD_QWERTY if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { config.keyboard = Configuration.KEYBOARD_QWERTY; keyboardPresence |= presenceFlag; } } } ...... // Determine whether a hard keyboard is available and enabled. boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; // 更新硬件鍵盤(pán)狀態(tài) if (hardKeyboardAvailable != mHardKeyboardAvailable) { mHardKeyboardAvailable = hardKeyboardAvailable; mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); } // 如果Setting中SHOW_IME_WITH_HARD_KEYBOARD被設(shè)置,將keyboard設(shè)置為KEYBOARD_NOKEYS,讓軟件盤(pán)可以顯示 if (mShowImeWithHardKeyboard) { config.keyboard = Configuration.KEYBOARD_NOKEYS; } ...... }
影響Configuration中keyboard的值有,
- 默認(rèn)值為KEYBOARD_NOKEYS,表示沒(méi)有外接鍵盤(pán)。
- 當(dāng)輸入設(shè)備為KEYBOARD_TYPE_ALPHABETIC時(shí),更新為KEYBOARD_QWERTY,一個(gè)標(biāo)準(zhǔn)鍵盤(pán)。
- 當(dāng)Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD為1時(shí),設(shè)置為KEYBOARD_NOKEYS,目的是讓軟鍵盤(pán)可以顯示。
inputflinger
接下來(lái)需要關(guān)注輸入設(shè)備時(shí)何時(shí)被設(shè)置KEYBOARD_TYPE_ALPHABETIC的。搜索代碼可以看到,這個(gè)flag實(shí)在native代碼中設(shè)置的,代碼在inputflinger/InputReader.cpp中。native和java使用了同一定義值,如果修改定義時(shí)需要注意同時(shí)修改。native中的名字為AINPUT_KEYBOARD_TYPE_ALPHABETIC。
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) { InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), controllerNumber, identifier, classes); ...... if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; } ...... return device; }
InputReader在增加設(shè)備時(shí),根據(jù)classes的flag來(lái)設(shè)置鍵盤(pán)類型。這個(gè)flag又是在EventHub.cpp中設(shè)置的。
status_t EventHub::openDeviceLocked(const char *devicePath) { ...... // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycodeLocked(device, AKEYCODE_Q)) { device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } ...... }
看到這里就比較明確了,在EventHub加載設(shè)備時(shí),如果輸入設(shè)備為鍵盤(pán),并且?guī)в?Q'鍵,就認(rèn)為這是一個(gè)標(biāo)準(zhǔn)的外接鍵盤(pán)。但為何判斷'Q'鍵還不是很清楚。
keylayout
上面說(shuō)道通過(guò)'Q'鍵來(lái)判斷是否為外接鍵盤(pán),這個(gè)'Q'鍵是Android的鍵值,鍵值是否存在是通過(guò)一個(gè)keylayout文件決定的。kl文件存儲(chǔ)在目標(biāo)系統(tǒng)的/system/usr/keylayout/下,系統(tǒng)可以有多個(gè)kl文件,根據(jù)設(shè)備的ID來(lái)命名。當(dāng)系統(tǒng)加載鍵盤(pán)設(shè)備時(shí),就會(huì)根據(jù)設(shè)備的Vendor ID和Product ID在/system/usr/keylayout/下尋找kl文件。例如一個(gè)kl文件名為”Vendor_0c45_Product_1109.kl“,表明設(shè)備的Vendor ID為0c45,Product ID為1109。一個(gè)kl的內(nèi)容示例如下,
key 1 BACK key 28 DPAD_CENTER key 102 HOME key 103 DPAD_UP key 105 DPAD_LEFT key 106 DPAD_RIGHT key 108 DPAD_DOWN key 113 VOLUME_MUTE key 114 VOLUME_DOWN key 115 VOLUME_UP key 142 POWER
鍵值映射需要使用關(guān)鍵之”key“進(jìn)行聲明,之后跟著的數(shù)字為L(zhǎng)inux驅(qū)動(dòng)中的鍵值定義,再后面的字符串是Android中按鍵的名稱。'Q'鍵是否存在完全取決于kl文件中是否有映射,而不是實(shí)際物理鍵是否存在。kl文件的查找也是有一個(gè)規(guī)則的,其查找順序如下,
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl /system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl /system/usr/keylayout/DEVICE_NAME.kl /data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl /data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl /data/system/devices/keylayout/DEVICE_NAME.kl /system/usr/keylayout/Generic.kl /data/system/devices/keylayout/Generic.kl
同時(shí)支持軟硬鍵盤(pán)
有了上面的知識(shí),就可以給出同時(shí)支持軟硬鍵盤(pán)的方案。
- 修改源碼邏輯,設(shè)置Configuration中keyboard的值為KEYBOARD_NOKEYS。這種Hack其實(shí)不好,破壞原生邏輯,缺乏移植性。非要這樣改的話,可以增加對(duì)設(shè)備的判斷,只有特定的鍵盤(pán)設(shè)備設(shè)置為KEYBOARD_NOKEYS,減少副作用。
- 修改keylayout,去掉'Q'鍵映射。有時(shí)kl文件寫(xiě)的不標(biāo)準(zhǔn),為了通用把所有鍵的映射都寫(xiě)上了,實(shí)際硬件鍵卻很少,我們就是這種情況。應(yīng)該按照真實(shí)硬件來(lái)編寫(xiě)kl文件。
- 設(shè)置Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD為1。我認(rèn)為這是最標(biāo)準(zhǔn)的修改方式,也非常方便。
關(guān)于第三個(gè)方案的修改方式有兩種,一種是修改缺省的setting值,在文件frameworks/base/packages/SettingsProvider/res/values/defaults.xml中增加,
<integer name="def_show_ime_with_hard_keyboard">1</integer>
另一種方式是在系統(tǒng)啟動(dòng)時(shí)在代碼中通過(guò)接口進(jìn)行設(shè)置。
Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1);
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:android自定義組件實(shí)現(xiàn)儀表計(jì)數(shù)盤(pán)
欄 目:Android
下一篇:AndroidQ 沙箱適配多媒體文件(小結(jié))
本文標(biāo)題:Android中外接鍵盤(pán)的檢測(cè)的實(shí)現(xiàn)
本文地址:http://mengdiqiu.com.cn/a1/Android/9049.html
您可能感興趣的文章
- 01-10Android自定義View之繪制圓形頭像功能
- 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方法詳解
- 01-10android實(shí)現(xiàn)記住用戶名和密碼以及自動(dòng)登錄
- 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
- 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代碼
- 01-10android實(shí)現(xiàn)指紋識(shí)別功能
- 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳解
- 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
- 01-10android開(kāi)發(fā)環(huán)境中SDK文件夾下的所需內(nèi)容詳解
- 01-10android異步消息機(jī)制 源碼層面徹底解析(1)


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10Android自定義View之繪制圓形頭像功能
- 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方
- 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
- 01-10android實(shí)現(xiàn)記住用戶名和密碼以及自動(dòng)
- 01-10C++自定義API函數(shù)實(shí)現(xiàn)大數(shù)相乘算法
- 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代
- 01-10android實(shí)現(xiàn)指紋識(shí)別功能
- 01-10如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
- 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
- 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳
隨機(jī)閱讀
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?