HoverEventを制御する


※エミュレータ環境では、USBマウスを接続出来ないため動作未検証の内容になります。

ICS(Android4.0)から、Viewに対するHover状態(マウスオーバー状態)を検知することが出来るようになりました。
Hover状態の検知とは、USBマウスなどのポインティングデバイスを接続した場合に表示されるカーソルが
Viewの上に乗っている状態がわかることです。

HoverEventを制御すると、画像イメージやボタンの上にカーソルが重なった時に、画像やボタンの色を変えるなど
以前よりもっとリッチなUIを作成できるようになります。

本エントリでは、APIDemosのHoverEventを取り扱ったサンプルコードを参考にしながら、
HoverEventの制御方法を紹介していきます。


画像はAPIDemoの Views → Hover Events のスクリーンショットです。

それでは、続きをどうぞ。

新しく追加されたメソッド

Hoverイベントを制御するため、ICSでは新たにViewクラスMotionEventクラスに以下表のAPIが追加されました。
新たに追加されたAPIを使用することで、特定のViewへのカーソルのHover状態を制御できます。

新規追加されたメソッド概要
ViewクラスのsetOnHoverListenerメソッドHoverEventのリスナ登録を行う
ViewクラスのonHoverメソッドHoverEventのリスナーメソッド
MotionEvent.ACTION_HOVER_ENTERHover状態の開始を表すアクション
MotionEvent.ACTION_HOVER_MOVEHover状態の継続を表すアクション
MotionEvent.ACTION_HOVER_EXITHover状態の停止を表すアクション

HoverEventの登録

ViewクラスのsetOnHoverListenerメソッドでリスナーを登録すると、
HoverEventの発生時に、ViewクラスのonHoverメソッドがコールされます。
onHoverメソッドは以下表の引数を持ちます。

引数概要
View vHoverEventが起こったView
MotionEvent eventActionの情報や座標情報、デバイスIDなどを持つ

ViewクラスのgetIdメソッドを利用することで対象のViewを判断し、
MotionEventクラスのgetActionメソッドにより取得できるAction状態毎に処理を振り分けることが可能になります。
それでは、APIDemosのサンプルのソースコードをみてみましょう。

APIDemosのサンプルでは、以下の処理を行っています。

  1. サンプル実行画面中央部の「Hover Here」部分のViewにHoverEventを設定する
  2. HoverEvent開始時から終了時までの間、TextViewにXY座標を表示する

1.サンプル実行画面中央部の「Hover Here」部分のViewにHoverEventのリスナーを設定する

setOnHoverListenerメソッドを利用して、HoverEventのリスナーを登録しています。

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.hover);

        mMessageTextView = (TextView) findViewById(R.id.message);
        mInterceptCheckBox = (CheckBox) findViewById(R.id.intercept_checkbox);
        mInterceptor = (HoverInterceptorView) findViewById(R.id.interceptor);

        View container = findViewById(R.id.container);
        container.setOnHoverListener(new View.OnHoverListener() {
            @Override
            public boolean onHover(View v, MotionEvent event) {
      // .....省略

登録したHoverEventのリスナーの中で、HoverEventの状態ごとに処理を振り分けています。

2.HoverEvent開始時から終了時までの間、TextViewにXY座標を表示する

3つのHoverEventの状態ごとに処理を行うため、MotionEventクラスのgetActionメソッドで状態を取得し各々の処理に振り分けています。

            public boolean onHover(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_HOVER_ENTER:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_entered_at,
                                event.getX(), event.getY()));
                        break;
                    case MotionEvent.ACTION_HOVER_MOVE:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_moved_at,
                                event.getX(), event.getY()));
                        break;
                    case MotionEvent.ACTION_HOVER_EXIT:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_exited_at,
                                event.getX(), event.getY()));
                        break;
                }
                return false;
            }

ソースコード中2行目、7行目、12行目にもあるように、
HoverEventで取得できる状態には、「ACTION_HOVER_ENTER」「ACTION_HOVER_MOVE」「ACTION_HOVER_EXIT」の3つの状態があります。

3つの状態は以下図のように、Viewにカーソルが乗った所がENTER、View上をカーソルが移動している間がMOVE、Viewからカーソルが抜けた状態がEXITとなります。

最後にサンプルのソースコードを添付します。
APIDemos/com/example/android/apis/view/Hover.java

public class Hover extends Activity {
    private TextView mMessageTextView;
    private CheckBox mInterceptCheckBox;
    private HoverInterceptorView mInterceptor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.hover);

        mMessageTextView = (TextView) findViewById(R.id.message);
        mInterceptCheckBox = (CheckBox) findViewById(R.id.intercept_checkbox);
        mInterceptor = (HoverInterceptorView) findViewById(R.id.interceptor);

        View container = findViewById(R.id.container);
        container.setOnHoverListener(new View.OnHoverListener() {
            @Override
            public boolean onHover(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_HOVER_ENTER:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_entered_at,
                                event.getX(), event.getY()));
                        break;
                    case MotionEvent.ACTION_HOVER_MOVE:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_moved_at,
                                event.getX(), event.getY()));
                        break;
                    case MotionEvent.ACTION_HOVER_EXIT:
                        mMessageTextView.setText(Hover.this.getResources().getString(
                                R.string.hover_message_exited_at,
                                event.getX(), event.getY()));
                        break;
                }
                return false;
            }
        });

        mInterceptCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mInterceptor.setInterceptHover(isChecked);
            }
        });
    }
}