AndroidのFocusとTouchMode

投稿日:  更新日:

Android端末はタッチスクリーンで操作することが当たり前になりました。昔あった、D-Padやトラックボールを装備した端末を見る事はありません。

そのせいもあって、最近はフォーカスを意識することが無くなったように思います。

しかし、アプリ開発を行っていると、まれに対応が必要になったりします。特にEditTextを扱うときなど。

必要な時に混乱しないようにフォーカスについてまとめてみました。

スポンサーリンク

フォーカスとは

以前、Android端末にD-Pad(Directional-Pad)やトラックボールが実装されている機種がありました。

どちらも、スクリーン上のViewを選んでアクション(クリックやタッチ)を起こす装備です。

D-Pad

D-Pad
TrackBall

TrackBall

D-Padであれば、スクリーン上のViewを十字ボタンを使って上下左右に移動していき、操作対象のViewに到達出来たら、中央のボタンを押してアクションを起こします。

このように、スクリーン上のViewを移動していくとき、現在どの位置のViewに移動できているのか、ユーザへ知らせる必要があります。次に移動する方向は現在の位置により決まるからです。

これを解決するため、Viewにフォーカスというパラメータを与えます。そして、フォーカスを取得したViewは色を変えるなどして、ユーザへ位置を知らせる方法を取ります。

フォーカスの移動

これがAndroidでいうところのフォーカスです。

※エミュレータでD-Pad使う方法は「Android EmulatorでD-Padをエミュレートする」を参照

スポンサーリンク

フォーカスを取得したViewの色を変える

フォーカスを取得したViewのバックグラウンドの色を変える例です。

フォーカスの状態(state_focused)を監視して色を変えるセレクター(focus_selector.xml)を作成して、Viewのbackground属性に設定します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    <color name="colorFocusTrue">#FFB1B1</color>
    <color name="colorFocusFalse">#FFE7E7</color>
    <color name="colorDefault">#DFDFDF</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@color/colorFocusTrue" />
<!--    <item android:state_focused="false" android:drawable="@color/colorFocusFalse"/>-->
    <item android:drawable="@color/colorDefault" />
</selector>
            <Button
                android:id="@+id/btnRC"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/focus_selector"
                android:text="Button" />
スポンサーリンク

フォーカスの属性

Viewはフォーカスを制御する2つの属性を持ちます。facusalbeとfocusableInTouchModeです。

Viewfocusablefocusable
InTouchMode
Viewfocusablefocusable
InTouchMode
TextViewfalsefalseExitTexttruetrue
ImageViewfalsefalseImageButtontruefalse
ButtontruefalseCheckBoxtruefalse
ToggleButtontruefalseRadioButtontruefalse
※View#isFocusableで取得した値

両者のデフォルト値は表のようになっていて、これによりフォーカスの扱われ方が変わってきます。※代表的なViewのみ記載

focusable属性

focusableは、Viewがフォーカスを取得できる(true)・できない(false)を指定する属性です。

図の矢印の向きに、D-Padの十字ボタンを使ってViewを移動してみました。フォーカスを取得したViewの色を変えています。

TextViewはfocusableがfalseのためフォーカスが取得できません(フォーカスが取得できないので、移動で飛ばされる)。よって、色が変わりません。

focusableInTouchMode属性

focusableInTouchModeは、Viewをタッチ(スクリーン上でタッチ)することでフォーカスが取得できる(true)・できない(false)を指定する属性です。

図の矢印の順番でViewをタッチしてみました。フォーカスを取得したViewの色を変えています。

EditTextはfocusableInTouchModeがtrueのためフォーカスの取得が出来ます。よって、色が変わります。※キーボードが開くのはアクションを伴うため(後述)


タッチスクリーンは目的のViewをタッチするだけで、直接Viewへフォーカスを与えられます。

よって、十字ボタンの操作でViewを移動させることも、フォーカスでユーザへViewの位置を知らせる必要もありません。

ちなみに、focusableInTouchMode属性は「TouchMode」という単語が属性名に入っていますが、タッチモード(後述)とは無関係です。

スクリーンのタッチはアクションを伴う

D-Padを使ったAndroidの操作は、フォーカスを取得したViewへアクションを起こす時に、ボタンの押下が必要でした。

タッチスクリーンの場合はタッチがアクションを伴います。

例えば、Buttonをタッチスクリーンでタッチした場合は、focusableInTouchModeがfalseのためフォーカスの取得はできませんが、onClick(クリックのアクション)は起動されます。

属性の変更と取得

focusableとfocusableInTouchMode属性はデフォルト以外の値へ変更できます。

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:text="TextView" />
        findViewById<TextView>(R.id.textView).setFocusable(true)
        findViewById<TextView>(R.id.textView).setFocusableInTouchMode(true)
        val _attr1 = findViewById<EditText>(R.id.editText).isFocusable
		val _attr2 = findViewById<EditText>(R.id.editText).isFocusableInTouchMode
		// ※↑ Booleanを返す

属性の変更と取得(≧API 26でView#setFocusable(Int)が追加された)

≧API 26において、View#setFocusable(Int)が追加になっています。
※View#setFocusable(Boolean)も生きています。廃止になっていません。

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="auto"
            android:focusableInTouchMode="true"
            android:text="TextView" />
        findViewById<TextView>(R.id.textView).setFocusable(view.FOCUSABLE_AUTO)
        al _attr = findViewById<TextView>(R.id.textView).focusable
		// ※↑ Intを返す

引数は次の通りです。

View.XXX動作
NOT_FOCUSABLEフォーカスできない
FOCUSABLEフォーカスできる
FOCUSABLE_AUTO
(デフォルト)
Viewの対話性に基づいて自動で決める
【対話性】
クリックできる(clickable属性:True)  ⇒フォーカスできる
クリックできない(clickable属性:false) ⇒フォーカスできない

FOCUSABLE_AUTOを指定すれば自動で決めてくれるようです。

スポンサーリンク

タッチモード

端末はタッチスクリーンにタッチが行われるとタッチモード(true)になり、D-PadやトラックボールでViewの移動が行われるとタッチモードでなくなり(false)ます。

タッチスクリーンを装備した端末はD-Padで操作することが無いのでtrueにしかならないと思います。

パラメータ値は次のように参照できます。

        val _touchMode1 = findViewById<TextView>(R.id.textView).isInTouchMode
		val _touchMode2 = findViewById<Button>(R.id.button).isInTouchMode
		val _touchMode3 = findViewById<ImageView>(R.id.imageView).isInTouchMode

タッチモードはViewのパラメータではなく、端末のパラメータです。

なのに、このパラメータ値はViewから取得できます。とても不思議なパラメータです。

スポンサーリンク
スポンサーリンク