このブログを始めて初めてのAndroid開発記事ということで、1番作成が困難だったServiceからソフトキーボードの表示・非表示を検知する処理を紹介しようと思います。
キーボードを検知する方法は全部で3つあり、すべて記事として紹介しようと思います。3つの中ではこの記事で紹介する検知方法が1番おすすめです。
動作確認はiWnn IME、POBox、Atok、Google日本語入力、Googleキーボード、FSKAREN for ASUSで行いました。
対応Androidバージョン
Android 4.0~Android 6.0
処理内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class SoftkeyboardMonitor { private InputMethodManager mInputMethodManager; private ActivityManager mActivityManager; private Set<String> mKeyboards = new HashSet<String>(); public SoftkeyboardMonitor(Context context) { mInputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); setKeyboards(); } public void setKeyboards() { mKeyboards.clear(); for (InputMethodInfo imi : mInputMethodManager.getInputMethodList()) { mKeyboards.add(imi.getServiceInfo().processName); } } public boolean isKeyboardShow() { List<ActivityManager.RunningServiceInfo> list = mActivityManager.getRunningServices(Integer.MAX_VALUE); if (list == null) { return false; } for (ActivityManager.RunningServiceInfo rsi : list) { if (mKeyboards.contains(rsi.process) && rsi.clientCount > 1) { return true; } } return false; } } |
この処理を説明すると、始めにsetKeyboards()を呼び出しAndroidにインストールされているキーボードのプロセス名をmKeyboards変数に格納します。
次に、isKeyboardShow()で現在動作しているServiceの一覧をlistに格納して、for文の中でキーボードのプロセス名と一致するものをチェックします。
キーボードの状態を判定しているのがrsi.clientCount > 1の部分で、キーボードが表示されていないときは1、キーボードが表示されているときは2を返します。
一応、端末によって2以上が返される可能性を考えて> 1という判定にしてあります。
isKeyboardShow()をTimerなどを使用し、一定時間おきに実行することで現在のキーボードの状態を取得できるようになります。
分かってしまうとこの程度のコードで実現できてしまいますが、探すのは一苦労でした。
この処理を使用したサンプルプロジェクトをGitHubにて公開しています。
sfapps-jp/Soft-Keyboard-Show-Hide-Monitoring
注意点
この処理はソフトキーボードからイベントが発生するわけではないので、自分の決めたタイミングでチェックを行う必要があります。
キーボードアプリがインストールされた場合はsetKeyboards()を呼び出してキーボードのリストを更新する必要があります。
対応Androidバージョンで書いたとおり、Android 2.3やAndroid 3.2などでは動作しないためご注意ください。
開発にあたって
開発前にネット上で調べたところキーボードの表示・非表示を検知する方法は下記のものがありました。
- レイアウトのonMeasureでレイアウトサイズを取得し、サイズが変化したことで判定する
- EditTextのOnFocusChangeListenerでフォーカスが当たったことを検知して判定する
- AccessibilityServiceを使いEditTextにフォーカスが当たったことを検知して判定する
AccessibilityServiceを利用した処理が一番やりたいことに近かったのですが、EditTextのクラスを継承して作成している場合にクラス名が一致しなくなり、個別に登録しなければいけないということだったため、この方法は断念しました。
StackOverflowを探してもほとんど情報はなかったので、このコードが役に立った方がいれば幸いです。
関連アプリ