GlassPaneを作る(その4)
ロック中でもボタンのイベントを処理するためにdispatch〜メソッドを書き換えているが、
if ( this.lock ) { return this.container.dispatchKeyEvent(event); }これだと期待通りボタンは動作するのだが、内部的には同一の親(GlassPane)に属している子孫のビューの間でフォーカスの移動が可能なため、矢印キー押下、トラックボール回転でブロックされているはずのビューにフォーカスが移ってしまうのだ。
解決しなければならないのは、ロック時とロック解除時にはフォーカス遷移を変えなくてはならないことだ。
ロック時 : フォーカスはFloatingビューとして配置されている範囲中でのみ遷移する
ロック解除時 : フォーカスは全ての(フォーカス可能な)ビューで遷移する
フォーカス遷移をロック時とロック解除時で変える必要がある訳だが、そんなことが可能なのだろうか。
- Activity (Window)におけるフォーカス遷移を動的に変える
以前にも書いたが、Androidのフォーカス遷移はView(ViewGroup)で管理されており、以下のようにビュー階層をトラバースしたリストの順で行われる。
View root = this.getWindow().getDecorView(); ArrayListfacusables = new ArrayList (); root.addFocusables(facusables, View.FOCUS_FORWARD, View.FOCUSABLES_ALL); Viewのフォーカスとトラバース
View#addFocusablesメソッドにより、facusablesはビュー階層全てを辿りフォーカス可能なビューをリストに追加していく。なので、このメソッドをオーバライドして(幸運なことにオーバライドできる)ロック時とロック解除時でフォーカス遷移の対象を変えることにした。
-
- GlassPane.java (addFocusablesをオーバライド)
@Override public void addFocusables(ArrayListviews, int direction, int focusableMode) { if ( this.isLock() ) { final int descendantFocusability = this.getDescendantFocusability(); if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { final int count = this.getChildCount(); for (int i = 0; i < count; i++) { final View child = this.getChildAt(i); if ( child == this.container && child.getVisibility() == View.VISIBLE) { child.addFocusables(views, direction, focusableMode); } } } } else { //ロックされていない場合は通常通り super.addFocusables(views, direction, focusableMode); } }
スーパークラス(ViewGroup)と違うのはロック状態を判定している点であり、
ロック中 : containerとその子孫のみ遷移の対象とする
ロック解除中 : 通常通り可視中の全てのビューをフォーカス対象とする
これによりフォーカス遷移対象を変えてリストを作成している。
なお、ViewGroup#getDescendantFocusabilityメソッドで取得できる定数である
FOCUS_BLOCK_DESCENDANTS FOCUS_AFTER_DESCENDANTS FOCUS_BEFORE_DESCENDANTS
これらはビュー自身へのフォーカスの遷移を、それぞれ行わない、子孫の後に遷移、子孫の前に遷移することを指定する。(既定値はFOCUS_BEFORE_DESCENDANTS)ロック中は子孫の遷移は行わないためここでは無視している。
これで、ロック中とロック解除中で希望通りのフォーカス遷移を実現できるようになった。
結局4話に渡ってしまったが機能を実現するための実装自体は、幸運なことにメソッドのオーバライドで解決しているので比較的簡単だったと思う。
先日はてなプラスの契約を更新したが、最大1GBまでファイルをアップロードできるようになったらしい。丁度良いのでテストも兼ねてここまで書いたGalssPane.javaをアップしておく。興味がある方はどうぞ。