ViewGroupには descendantFocusability
というものがあります。これはViewGroupのフォーカスの挙動を設定することができます。
通常のアプリではほとんど意識する必要はないのですが、例えばTVアプリのようにリモコン操作するようなアプリの場合は知っておくと良いかもです。
descendantFocusability には3つの設定値があります。
- afterDescendants
- beforeDescendants
- blocksDescendants
これによってどのような挙動が変わるかを見ていきます。
afterDescendants
これはViewGroupの中の子Viewがフォーカスを取得できるかどうかによって動作が変わってきます。
最初に以下のようなレイアウトを見てみます。
基本ViewGroupはデフォルトで android:focusable=”false”
なので、フォーカスが当たるように true に設定します。
Buttonはデフォルトで android:focusable=”true”
になっています。この例では子Viewがフォーカスを受け取ることができる状態です。
このときは、ViewGroup(この例ではConstraintLayout)はフォーカスを当てることができません。 コードからrequestFocus
を使ったとしてもViewGroupではなく最初の子Viewのほうにフォーカスが当たります。
次に以下のレイアウトを見てみます。
今度は子Viewがフォーカスを取得でいない状態になっています。
このときはViewGroupのほうにフォーカスが当たるようになります。requestFocus
によってフォーカスを当てることも出来ます。
afterDescendantsをまとめると以下の挙動になります。
- 子Viewがフォーカスを受け取れる → ViewGroupは受け取れない
- 子Viewがどれもフォーカスを受け取れない → ViewGroupは受け取れる
beforeDescendants
こちらはデフォルトの値になっています。
上の例ではわざと設定していますが、特に指定しなくても大丈夫です。
こちらは普通にフォーカスを取得することが可能な状態です。コードからrequestFocus
することでフォーカスを当てることも可能になっています。
ただ、リモコンなどによるフォーカス移動の場合は、レイアウトの配置などによっては受け取ったり受け取らなかったりします。
実行して実際に確認しないと分かりにくい部分でもあります。うまくフォーカスが当たらない場合などは、 android:nextFocusDown
などを使って明示的に指定するとよいと思います。
blocksDescendants
これは子Viewがフォーカスを取得できる状態であったとしても、フォーカスが当たらなくなります。
この場合、Buttonにフォーカスが当たらなくなります。 requestFocus
も効かなくなります。
もし、ViewGroupが android:focusable=”true”
の場合は、ViewGroup自体はフォーカスを取得できますが、子Viewについてはフォーカスは取得できません。
一括してViewGroupの子Viewのフォーカスを無効にしたいときなどは便利です。
複雑な画面の場合、フォーカス制御はかなり大変なものになりますが、このあたりをうまく使ってやっていくと良いと思います。