たかがフォーカス,されどフォーカス
フォーカスとタッチモード
Androidのフォーカスの制御は、タッチモードがからんでいてわかりずらい。
タッチモードについては、「l. タッチモード - ソフトウェア技術ドキュメントを勝手に翻訳」を参照。
非タッチモード時とタッチモード時で、異なるAPIが存在する。
Activity画面にTextViewと3つのEditViewとButtonを配置する、以下のようなレイアウトファイルを作成する。
リスト1(main.xml)
このプログラムを実行して、各ウィジェットをタッチ(クリック)した場合のフォーカスの移動を確認してみる。
TextViewをタッチしてもフォーカスは、TextViewに移動しない。
EditTextの場合は、タッチする事でフォーカスを移動する事ができる。
Buttonの場合にはタッチした瞬間、一時的にフォーカスが移動するが、その後、タッチ前のフォーカスの位置に戻ってしまう。
タッチではなく、トラックボール(DPad)をUP,DOWNすると、EditTextとButtonにはフォーカスが移動するが、TextViewには移動しない事が確認できる。
このタッチモードと非タッチモードでの動作の違いは、3種類のウィジェットのfocusable属性とfocusableInTouchMode属性のデフォルト値の違いによる。
ウィジェットのfocusable属性とfocusableInTouchMode属性の値は、 View#isFocusableとView#isFocusableInTouchModeメソッドにて確認する事ができる。
次のプログラムを用いて、TextViewとEditTextとButtonのデフォルトのfocusable属性とfocusableInTouchMode属性を確認してみる。
リスト2(FocusSample1Activity.java)
このプログラムを実行してボタンを押下すると、以下の図のようにLogCatビューに、各ウィジェットのfocusable属性とfocusableInTouchMode属性の値が出力される。
focusable属性がtrueのウィジェットは、非タッチモード時にフォーカスを取得可能なのに対して、 focusableInTouchMode属性がtrueの場合はタッチモード時にフォーカスを取得可能となる。
つまり、TextViewの場合には、focusable属性とfocusableInTouchMode属性ともにfalseのため、 非タッチモード時,タッチモード時ともにフォーカスを受け取る事ができない。
EditTextの場合は、focusable属性とfocusableInTouchMode属性ともにtrueのため、どちらのモードでもフォーカスを受け取る事ができる。
Buttonの場合には、focusable属性はtrueであるため非タッチモードではフォーカスを受け取る事ができるが、 focusableInTouchMode属性がfalseのため、タッチモードになった瞬間フォーカスを失ってしまう事になる。
次に、リスト1のレイアウトファイルを以下のように変更して、 TextViewとButtonも、非タッチモード,タッチモード時にフォーカスを受け取れるように、 また、2番目のEditText(editText1)のみ非タッチモードでフォーカスを受け取れないように 修正する。
- TextViewのfocusable属性とfocusableInTouchMode属性ともにtrueに設定
- 2番目のEditTextのみfocusable属性をfalseに設定
- ButtonのfocusableInTouchMode属性をtrueに設定
リスト3-レイアウトファイルのfocusable属性とfocusableInTouchMode属性を修正(main.xml)
このレイアウトファイルを使ってリスト2のコードを実行して、focusable属性とfocusableInTouchModeの値を確認してみると、以下のようになる。
ここで、面白いのは、editText2のfocusable属性だけをfalseに設定したのに、 focusableInTouchMode属性もfalseに設定されてしまっている。
それ以外は設定どおりの値に変更されている。
ちなみにxmlではなく、コードでfocusable属性とfocusableInTouchMode属性を変更するには、 View#setFocusableとView#setFocusableInTouchModeメソッドを使う。
実際の動作を確認してみる。
TextViewのfocusable属性とfocusableInTouchMode属性をtrueに設定されているにもかかわらず、 TextViewにフォーカスを移しても表示はかわらない。
これは、TextViewは文字列を表示するだけのウィジェットなので、フォーカスを受け取る意味が無いからと思われる。
2番目のEditText(editText2)はfocusable属性,focusableInTouchMode属性ともにfalseになったので、 カーソルを移動してもフォーカスを受け取る事ができず、非タッチモードの場合には次のウィジェットにフォーカスが移動する。
Buttonの場合は、focusableInTouchMode属性をtrueに設定したので、タッチ時にもフォーカスを受け取る事ができるようになった。
以上のように、focusable属性とfocusableInTouchMode属性を変更することで、 ウィジェットのデフォルトのフォーカス可能状態を変更できるが、 これを変更するのは好ましい動作と言えないので、通常は変更しない方が良いだろう。
フォーカスの設定
View#requestFocusメソッドを使うと、指定のウィジェットにフォーカスを移動させる事ができる。
しかし、タッチモードの時には、 focusableInTouchMode属性がfalseのウィジェットにフォーカスを移動させることができないので、 このメソッドではフォーカスを移動させる事はできない。
View#requestFocusFromTouchメソッドを使うと、 タッチモード時でもfocusableInTouchMode属性がfalseのウィジェットに対して、 フォーカスを移動させることができる。
この、requestFocusメソッドとrequestFocusFromTouchメソッドとfocusableInTouchMode属性の関係 を確認するためのコードを以下に示す。
リスト4(FocusSample2Activity.java)
このコードは、EditTextウィジェットに対して「A」キーが押された時と、 EditTextがタッチされた時に、強引にフォーカスをButtonに移動させる事を意図したプログラムである。
しかし、同じrequestFocusメソッドを実行しているにもかかわらず、 「A」キーが押された時にはフォーカスが移動するが、タッチされた時にはフォーカスが移動しない。
これは、EditTextがタッチされた時にタッチモードに移行してしまうためである。
この問題を回避するには、ButtonのfocusableInTouchMode属性をtrueに設定してしまう事である。
そうすれば、タッチモード時でもButtonはフォーカスを受け取れるようになる。
しかし、ButtonのfocusableInTouchMode属性をtrueにしたくない場合には、 もう一つの解決方法として、39行目のrequestFocusメソッドの換わりにrequestFocusFromTouchメソッドを使う。
requestFocusメソッドには requestFocus(int direction) ,requestFocus(int, android.graphics.Rect) というオーバーロードされたメソッドもある。
イベント
フォーカスが変更された時のイベントを捕捉するには、OnFocusChangeListenerを使う。
以下のプログラムはその例です。
リスト5(FocusSample4Activity.java)
このプログラムを実行して、ウィジェットのフォーカスを変更すると、LogCatビューは下記のように表示される。
フィーカスに関するその他のトピック
xmlでフォーカスを設定
xmlレイアウトファイルにrequestFocusタグを指定する事で、フォーカスを設定する事もできる。
「Androidアプリで、起動後最初にカーソルを当てるViewを指定する方法 | mucchinのAndroid戦記」 , 「7.5.4 レイアウトリソース - ソフトウェア技術ドキュメントを勝手に翻訳」 を参照。
フォーカスを受け取る順番の制御
フォーカス移動の順番を変更したい場合は、nextFocusUp属性,nextFocusDown属性,android:nextFocusLeft属性,nextFocusRight属性を使う。
「フォーカスを制御する - Androidプログラマへの道 ~ Moonlight 明日香 ~ - livedoor Wiki(ウィキ)」 を参照。
「6.5 UIイベントハンドリング - ソフトウェア技術ドキュメントを勝手に翻訳」も参照。
Enterキーによるフォーカスの移動
EditTextにsingleLine属性を指定すると、そのEditTextはエンターキーでフォーカスを移動するようになる。
リスト6(main.xml)
その他
「Viewのフォーカスとトラバース - Kazzzの日記」や 「ソフトキーボードの制御 | GE Android Blog」 も参考になりそう。