Java道|マウスイベント処理の基本

マウスが近づくだけで、画面がそっと反応する。
マウスイベント処理を学ぶと、JavaのGUIアプリは「押したら動く画面」から「ユーザーの動きに合わせて反応する画面」へ広がります。

前回までのGUIイベント処理では、ボタンを押したときに文字を変えるような、分かりやすいイベント処理を学びました。

ボタンを押す。
イベントが発生する。
登録されたリスナに通知される。
actionPerformed が呼ばれる。
画面の表示が変わる。

この流れは、GUIイベント処理の基本としてとても大切です。

ただし、GUIアプリケーションで扱えるイベントは、ボタンをクリックしたときだけではありません。

マウスカーソルが部品の上に入った。
マウスカーソルが部品の外へ出た。
マウスボタンを押した。
マウスボタンを離した。
クリックした。

このように、マウスの細かな動きもイベントとして扱えます。

鬼滅の刃風にたとえると、鬼殺隊本部の修行案内盤に「修行場へようこそ」と書かれた札があるとします。

若い女性の和風剣士がその札に手を近づけると、案内札の表示が「修行メニューを選んでください」に変わる。
手を離すと、また「修行場へようこそ」に戻る。

このように、ボタンを押さなくても、マウスが部品に近づいたり離れたりするだけで、画面を反応させることができます。

この仕組みが、マウスイベント処理です。

この記事では、マウスがボタンの上に入ったときと、外へ出たときにボタンの文字を切り替える Sample4.java を使い、MouseListener、MouseEvent、addMouseListener、mouseEntered、mouseExited の役割を整理します。さらに、コードを読みやすくする MouseAdapter、無名クラス、ラムダ式との違いも確認していきます。

マウスイベントとは何か

マウスイベントとは、マウス操作によって発生するイベントです。

GUIでは、マウスの動きがかなり細かく区別されます。

たとえば、次のようなものがあります。

マウスの動き対応するメソッド意味
クリックしたmouseClicked押して離す一連の操作が行われた
ボタンを押したmousePressedマウスボタンを押し込んだ
ボタンを離したmouseReleased押していたマウスボタンを離した
部品の上に入ったmouseEnteredマウスカーソルが部品の領域に入った
部品の上から出たmouseExitedマウスカーソルが部品の領域から出た

鬼滅の刃風にすると、修行案内札に対する動きは次のように考えられます。

メソッド鬼滅の刃風のイメージ
mouseEntered隊士の手が修行案内札の上に近づいた
mouseExited隊士の手が修行案内札から離れた
mousePressed修行案内札を押し込んだ
mouseReleased押していた手を離した
mouseClicked押して離す操作が完了した

このように、マウスイベントでは「クリックしたかどうか」だけでなく、部品に入った、外に出た、押した、離したという動きまで扱えます。

そのため、GUI画面に自然な反応を持たせたいときに、とても役立ちます。

たとえば、マウスがボタンに重なったときだけ説明を表示する、マウスが外れたら元の表示に戻す、といった動きが作れます。

ボタンを押さなくても画面は反応できる

ボタンイベントでは、主にクリックや押下がきっかけになります。

しかし、マウスイベントでは、クリックする前の段階にも反応できます。

たとえば、ボタンの上にカーソルが乗っただけで表示を変えられます。

状態ボタンの表示
最初の表示修行場へようこそ
マウスがボタンに入る修行メニューを選んでください
マウスがボタンから出る修行場へようこそ

この動きがあると、ユーザーは「この部品は反応している」「ここに操作できるものがある」と分かりやすくなります。

鬼滅の刃風に言えば、修行案内札に手を近づけた瞬間、札が青白く光って案内文が変わるようなものです。

押してから初めて反応するのではなく、近づいた時点で案内してくれるため、より親切な修行装置になります。

マウスイベント処理の3つの役割

マウスイベント処理でも、基本の構造は前回のボタンイベントと同じです。

大切なのは、ソース、イベント、リスナの3つです。

役割内容今回の例
ソースイベントが発生する部品Button bt
イベント実際に起きた出来事MouseEvent
リスナイベントを受け取って処理する側SampleMouseListener

今回の場合、イベントを発生させるのは bt というボタンです。

マウスがボタンの上に入ったり、外へ出たりすると MouseEvent が発生します。

その MouseEvent を受け取るのが SampleMouseListener です。

鬼滅の刃風に置き換えると、次のようになります。

イベント処理の用語鬼滅の刃風のイメージ
ソース修行案内札
MouseEvent手が近づいた、離れたという通知札
MouseListener通知を受け取る見張り係
mouseEntered手が近づいたときの処理
mouseExited手が離れたときの処理

つまり、修行案内札そのものがすべての処理をするわけではありません。

札は「マウスが入った」「マウスが出た」という出来事を発生させます。

そして、その出来事を受け取るリスナが、表示変更などの処理を行います。

図:マウスイベント処理の基本構造

↓クリックすると拡大表示されます。

この図が示していること

この図では、マウスイベント処理の基本構造を表しています。

Button がソースとしてマウスイベントを発生させます。

マウスがボタンの上に入ったり、ボタンの外へ出たりすると MouseEvent が発生します。

その MouseEvent が登録済みの MouseListener に通知され、mouseEntered や mouseExited が呼ばれます。

図の要素意味
Button btマウスイベントが発生する部品
MouseEventマウス操作で発生した出来事の情報
MouseListenerマウスイベントを受け取る処理担当
mouseEnteredマウスが部品に入ったときに呼ばれる
mouseExitedマウスが部品から出たときに呼ばれる
setLabelボタンの表示文字を変更する

この図から分かることは、マウスイベントも「ソースがイベントを発生させ、リスナが受け取り、対応するメソッドで処理する」という基本構造で動くということです。

アダプタクラスを使って簡略化する

ファイル名:Sample4.java

import java.awt.*;
import java.awt.event.*;

public class Sample4 extends Frame
{
    private Button bt;

    public static void main(String[] args)
    {
        Sample4 sm = new Sample4();
    }

    public Sample4()
    {
        super("鬼殺隊 修行案内パネル");

        // ボタンを作成する
        bt = new Button("修行場へようこそ");

        // フレームにボタンを追加する
        add(bt);

        addWindowListener(new SampleWindowListener());

        // ボタンに対するマウスイベントを登録する
        bt.addMouseListener(new SampleMouseListener());

        setSize(320, 200);
        setVisible(true);
    }

    class SampleWindowListener extends WindowAdapter
    {
        public void windowClosing(WindowEvent e)
        {
            System.exit(0);
        }
    }

    class SampleMouseListener implements MouseListener
    {
        public void mouseClicked(MouseEvent e)
        {
        }

        public void mouseReleased(MouseEvent e)
        {
        }

        public void mousePressed(MouseEvent e)
        {
        }

        public void mouseEntered(MouseEvent e)
        {
            bt.setLabel("修行メニューを選んでください");
        }

        public void mouseExited(MouseEvent e)
        {
            bt.setLabel("修行場へようこそ");
        }
    }
}

このプログラムでできること

このプログラムを実行すると、「鬼殺隊 修行案内パネル」というタイトルのウィンドウが表示されます。

ウィンドウの中には、「修行場へようこそ」と書かれたボタンが表示されます。

そして、マウスカーソルがそのボタンの上に入ると、ボタンの文字が「修行メニューを選んでください」に変わります。

さらに、マウスカーソルがボタンの外へ出ると、文字は「修行場へようこそ」に戻ります。

状態ボタンの表示
最初の表示修行場へようこそ
マウスがボタンに入る修行メニューを選んでください
マウスがボタンから出る修行場へようこそ

このプログラムの特徴は、ボタンを押さなくても表示が変わることです。

マウスカーソルの位置だけで、画面が反応します。

鬼滅の刃風に言えば、修行案内札に手を近づけると「修行メニューを選んでください」と案内が出て、手を離すと「修行場へようこそ」に戻る修行装置です。

Buttonを作ってFrameに追加する

まず、ボタンは Button クラスで作ります。

bt = new Button("修行場へようこそ");

このコードによって、「修行場へようこそ」と表示されたボタンオブジェクトが作られます。

ただし、ボタンを作っただけではウィンドウには表示されません。

Frame に追加するために add を使います。

add(bt);

この2つの処理は、GUI部品を扱う基本です。

コード役割
new Button("修行場へようこそ")ボタン部品を作る
add(bt)ボタンをFrameに追加する

鬼滅の刃風に言えば、new Button は修行案内札を作る作業です。

add(bt) は、その修行案内札を本部の案内盤に取り付ける作業です。

案内札を作っただけでは、隊士の目の前には出てきません。
案内盤に取り付けて、はじめて操作できる部品になります。

addMouseListenerでマウスイベントを登録する

ボタンにマウス操作へ反応させるには、リスナを登録する必要があります。

Sample4.java では、次のように書いています。

bt.addMouseListener(new SampleMouseListener());

この1行は、次のような意味です。

このボタンでマウスイベントが起きたら、SampleMouseListener に知らせてください。

Button と MouseListener を結びつけるのが addMouseListener です。

部分役割
btマウスイベントが発生するボタン
addMouseListenerボタンとリスナを結びつける登録
new SampleMouseListener()マウスイベントを受け取るリスナオブジェクト

鬼滅の刃風に言えば、修行案内札と見張り係のあいだに連絡線をつなぐようなものです。

この登録があるから、隊士の手が札に近づいた瞬間や離れた瞬間に、見張り係が反応できます。

MouseListenerを使う理由

MouseListener は、マウスに関するイベントを受け取るためのインターフェイスです。

Sample4.java では、内部クラス SampleMouseListener が MouseListener を実装しています。

class SampleMouseListener implements MouseListener

MouseListener を実装すると、マウス操作に応じたメソッドを定義できます。

今回特に使っているのは、次の2つです。

メソッド役割
mouseEnteredマウスが部品の上に入ったときに呼ばれる
mouseExitedマウスが部品の上から出たときに呼ばれる

鬼滅の刃風に言えば、MouseListener は「修行案内札に手が近づいたか、離れたかを見張る係」です。

手が近づいたら mouseEntered が動きます。
手が離れたら mouseExited が動きます。

この2つを使うことで、マウスの位置に合わせてボタンの表示を変えられます。

MouseListenerでは5つのメソッドが必要になる

MouseListener を実装する場合、次の5つのメソッドをすべて定義する必要があります。

メソッド名意味
mouseClickedクリックしたとき
mousePressed押したとき
mouseReleased離したとき
mouseEntered部品の上に入ったとき
mouseExited部品の上から出たとき

今回のプログラムで実際に処理を書いているのは、mouseEntered と mouseExited だけです。

しかし、MouseListener を実装する以上、使わないメソッドも空の形で書く必要があります。

public void mouseClicked(MouseEvent e)
{
}

public void mouseReleased(MouseEvent e)
{
}

public void mousePressed(MouseEvent e)
{
}

これは少し面倒に感じるところです。

本当にやりたいことは、マウスが入ったら文字を変える、マウスが出たら文字を戻す、という2つだけです。

それでも MouseListener を直接実装する場合は、5つすべてのメソッドを書く必要があります。

mouseEnteredとmouseExitedの役割

今回の処理の中心は、mouseEntered と mouseExited です。

public void mouseEntered(MouseEvent e)
{
    bt.setLabel("修行メニューを選んでください");
}

public void mouseExited(MouseEvent e)
{
    bt.setLabel("修行場へようこそ");
}

mouseEntered は、マウスカーソルがボタンの上に入った瞬間に呼ばれます。

その中で、bt.setLabel を使って、ボタンの文字を「修行メニューを選んでください」に変えています。

mouseExited は、マウスカーソルがボタンの上から出た瞬間に呼ばれます。

その中で、bt.setLabel を使って、ボタンの文字を「修行場へようこそ」に戻しています。

メソッド呼ばれるタイミング実行する処理
mouseEnteredマウスがボタンに入ったとき修行メニューを選んでください に変更
mouseExitedマウスがボタンから出たとき修行場へようこそ に戻す

鬼滅の刃風に言えば、修行案内札に手を近づけると詳細案内が出て、手を離すと通常案内に戻る流れです。

図:マウスが入ったとき・出たときの表示変更

↓クリックすると拡大表示されます。

この図が示していること

この図では、マウスカーソルがボタンの上に入ったときと、外へ出たときに、ボタンの表示が変わる流れを表しています。

最初は「修行場へようこそ」と表示されています。

マウスがボタンの上に入ると mouseEntered が呼ばれ、「修行メニューを選んでください」に変わります。

マウスがボタンの外へ出ると mouseExited が呼ばれ、「修行場へようこそ」に戻ります。

状態呼ばれるメソッド表示
最初なし修行場へようこそ
マウスが入るmouseEntered修行メニューを選んでください
マウスが出るmouseExited修行場へようこそ

この図から分かることは、マウスイベントを使うと、クリックしなくてもマウスの位置の変化だけでGUIを反応させられるということです。

MouseEventの役割

MouseListener の各メソッドには、MouseEvent e という引数があります。

public void mouseEntered(MouseEvent e)

MouseEvent は、マウス操作で発生したイベントの情報を持つオブジェクトです。

今回のプログラムでは e を直接使っていません。

しかし、MouseEvent の中には、マウス操作に関する情報が入っています。

たとえば、発生した位置や、どの部品上で起きたイベントかなどを調べるときに使えます。

要素役割
MouseEventマウスイベントの情報を持つオブジェクト
e発生したイベント情報を受け取る変数
mouseEntered(MouseEvent e)マウスが入った情報を受け取りながら処理する
mouseExited(MouseEvent e)マウスが出た情報を受け取りながら処理する

学習の最初では、まず次のように押さえれば十分です。

マウス操作が起きると、MouseEvent が作られ、対応するメソッドに渡される。

鬼滅の刃風に言えば、MouseEvent は「手が近づいた」「手が離れた」といった情報が書かれた通知札です。

今回の Sample4.java では、その通知札の詳しい中身までは読まず、通知が来たことをきっかけにボタンの表示を変えています。

ソース・イベント・リスナの流れ

Sample4.java のイベント処理の流れを、もう一度整理します。

要素今回の内容
ソースbt というボタン
イベントMouseEvent
リスナSampleMouseListener
実際の処理mouseEntered、mouseExited
登録bt.addMouseListener(new SampleMouseListener())

流れは次のようになります。

順番起こること
1マウスカーソルがボタンの上に入る
2ボタンが MouseEvent を発生させる
3登録済みの SampleMouseListener に通知される
4mouseEntered が呼ばれる
5ボタンの文字が変わる
6マウスカーソルがボタンの外へ出る
7mouseExited が呼ばれる
8ボタンの文字が元に戻る

このように、ボタンイベントと同じく、マウスイベントでも登録の考え方が重要です。

部品とリスナを結びつけておくことで、マウス操作が起きたときに処理が呼び出されます。

addMouseListenerの意味

マウスイベント処理で特に大切なのが addMouseListener です。

bt.addMouseListener(new SampleMouseListener());

この1行は、ボタンとマウスイベント処理担当を結びつけています。

つまり、次のような設定です。

このボタンでマウス操作が起きたら、SampleMouseListener に知らせてください。
状態どうなるか
ボタンだけあるマウスイベントの通知先が決まっていない
リスナだけあるどの部品のイベントを受けるか決まっていない
addMouseListenerで登録するボタンのマウスイベントをリスナが受け取れる

鬼滅の刃風に言えば、修行案内札と見張り係の間に連絡線をつなぐようなものです。

連絡線があるから、手が近づいた瞬間に見張り係へ通知され、案内表示を変えられます。

イベント処理が長くなりやすい理由

MouseListener は便利ですが、少しコードが長くなりやすい面があります。

理由は、使わないメソッドも含めて5つすべてを書かなければならないからです。

今回、本当に必要な処理は次の2つです。

やりたいこと使うメソッド
マウスが入ったら文字を変えるmouseEntered
マウスが出たら文字を戻すmouseExited

しかし、MouseListener を直接実装する場合は、次の3つも空で書く必要があります。

今回は使わないメソッド空で書く理由
mouseClickedMouseListenerの約束に含まれるため
mousePressedMouseListenerの約束に含まれるため
mouseReleasedMouseListenerの約束に含まれるため

そのため、コードを読んだときに「大事な処理はどこにあるのか」が少し見えにくくなることがあります。

学習段階では、5つのメソッドをすべて見ることで、MouseListener の全体像を理解できます。

ただし、実際に必要な処理だけをすっきり書きたい場合には、別の書き方が便利です。

それが MouseAdapter です。

MouseAdapterを使う簡略化

MouseAdapter は、マウスイベント処理を簡単に書くためのアダプタクラスです。

MouseListener を直接実装すると、5つのメソッドをすべて書く必要があります。

一方、MouseAdapter を継承すると、必要なメソッドだけを書けます。

たとえば、今回の処理なら、次のように書けます。

bt.addMouseListener(new SampleMouseAdapter());

class SampleMouseAdapter extends MouseAdapter
{
    public void mouseEntered(MouseEvent e)
    {
        bt.setLabel("修行メニューを選んでください");
    }

    public void mouseExited(MouseEvent e)
    {
        bt.setLabel("修行場へようこそ");
    }
}

この形なら、mouseClicked、mousePressed、mouseReleased を空で書く必要がありません。

書き方特徴
MouseListener を直接実装5つ全部のメソッドを書く必要がある
MouseAdapter を継承必要なメソッドだけ書けばよい

鬼滅の刃風に言えば、MouseListener を直接実装する方法は、修行札のすべての反応項目を一覧で準備する方法です。

MouseAdapter は、その中から今回必要な「手が近づいた」「手が離れた」だけを取り出して使える補助装置のようなものです。

無名クラスを使う簡略化

イベント処理専用の小さなクラスを、毎回名前付きで作るほどでもない場合があります。

そのようなときに使いやすいのが無名クラスです。

MouseAdapter を無名クラスとして使うと、次のように書けます。

bt.addMouseListener(new MouseAdapter()
{
    public void mouseEntered(MouseEvent e)
    {
        bt.setLabel("修行メニューを選んでください");
    }

    public void mouseExited(MouseEvent e)
    {
        bt.setLabel("修行場へようこそ");
    }
});

この書き方では、addMouseListener で登録している場所の近くに、そのまま処理内容を書けます。

そのため、ボタンと処理の関係が見えやすくなります。

無名クラスのよさ内容
小さな処理を近くに書ける登録と処理の対応が見やすい
クラス名を考えなくてよい一度だけ使う処理に向いている
イベント処理をまとめやすい短い処理なら読みやすくなる

ただし、処理が長くなると、無名クラスの中身が大きくなり、かえって読みにくくなることもあります。

小さなイベント処理には向いていますが、複雑な処理なら名前付きクラスに分けたほうが整理しやすいです。

ラムダ式について知っておきたいこと

ラムダ式も、イベント処理を簡潔に書く方法としてよく使われます。

たとえば、ActionListener のように抽象メソッドが1つだけのインターフェイスでは、ラムダ式を使いやすいです。

ボタンのクリック処理なら、次のように書けます。

bt.addActionListener(e -> bt.setLabel("気を高めています"));

ラムダ式の基本形は、次のように考えます。

引数 -> 処理

処理が複数行ある場合は、波かっこを使います。

e -> {
    bt.setLabel("気を高めています");
    System.out.println("修行を開始しました。");
}

ラムダ式のよいところは、小さな処理を短く書けることです。

ラムダ式のよさ内容
コードが短くなる小さなイベント処理をすっきり書ける
処理の流れが見えやすい登録と処理を近くに書ける
ActionListenerと相性がよいactionPerformedだけなので書きやすい

ただし、ここで大切な注意点があります。

MouseListener は、抽象メソッドを複数持つインターフェイスです。

そのため、MouseListener そのものをラムダ式だけで直接書くことはできません。

インターフェイスラムダ式で書きやすいか理由
ActionListener書きやすい抽象メソッドが actionPerformed の1つ
MouseListener直接は書けない抽象メソッドが複数ある

今回のように mouseEntered と mouseExited を扱う場合は、MouseListener をそのまま実装するか、MouseAdapter を使う方法が基本です。

鬼滅の刃風に言えば、ActionListener は「押されたら反応する」という1つの役割だけなので、短い指令札で済みます。

一方、MouseListener は「入る」「出る」「押す」「離す」「クリックする」という複数の見張り項目を持つため、ラムダ式だけで一言にまとめることはできません。

図:MouseListener、MouseAdapter、無名クラス、ラムダ式の使い分け

↓クリックすると拡大表示されます。

この図が示していること

この図では、マウスイベント処理の書き方を比較しています。

MouseListener を直接実装する方法は、5つのメソッドをすべて書くため、基本構造を学ぶのに向いています。

MouseAdapter を使うと、必要なメソッドだけを書けます。

無名クラスを使うと、登録場所の近くに処理をまとめられます。

ラムダ式は ActionListener のような抽象メソッドが1つだけのインターフェイスでは便利ですが、MouseListener のように複数メソッドを持つインターフェイスには直接使えません。

書き方向いている場面
MouseListenerを直接実装基本構造をしっかり理解したいとき
MouseAdapter必要なメソッドだけを書きたいとき
無名クラス短い処理を登録場所の近くに書きたいとき
ラムダ式ActionListenerなど関数型インターフェイスを簡潔に書きたいとき

この図から分かることは、イベント処理には複数の書き方があり、目的に応じて使い分けるとコードを読みやすくできるということです。

どの書き方を選べばよいか

イベント処理の書き方は、目的によって使い分けると理解しやすくなります。

書き方向いている場面
インターフェイスを直接実装学習用として基本構造をしっかり理解したいとき
アダプタクラス必要なメソッドだけを書きたいとき
無名クラス短い処理をその場でまとめたいとき
ラムダ式ActionListenerなど関数型インターフェイスで簡潔に書きたいとき

今回の Sample4.java では、MouseListener を直接実装しています。

この方法は少し長くなりますが、マウスイベントには5つの種類があることを理解しやすいです。

最初にしくみを学ぶ段階では、とてもよい形です。

慣れてきたら、MouseAdapter や無名クラスを使って、必要な処理だけをすっきり書けるようになります。

高度なイベント処理で見えてくること

マウスイベントを扱えるようになると、GUIアプリにより自然な反応を持たせられます。

クリックしたときだけでなく、近づいたとき、離れたとき、押している間、離した瞬間などに反応できます。

たとえば、次のような動きが作れるようになります。

マウス操作GUIの反応例
マウスが重なる説明を表示する
マウスが外れる説明を消す
押している間ボタンの見た目を変える
離した瞬間操作を確定する
位置が変わる位置に応じて表示を変える

鬼滅の刃風に言えば、修行装置が「札を押したとき」だけでなく、「手が近づいたとき」「手が離れたとき」「押し込んでいるとき」まで見分けられるようになる状態です。

これにより、アプリはより柔らかく、使う人の動きに寄り添った画面になります。

マウスイベント処理で大切な要素

今回の内容で大切な要素を整理すると、次の通りです。

要素内容
MouseListenerマウスイベントを受け取るためのインターフェイス
MouseEventマウス操作で発生したイベント情報
addMouseListenerソースとリスナを結びつける登録
mouseEnteredマウスが部品に入ったときに呼ばれる
mouseExitedマウスが部品から出たときに呼ばれる
setLabelボタンの表示文字を変更する
MouseAdapter必要なメソッドだけを書ける補助クラス
無名クラス小さなイベント処理をその場で書ける方法
ラムダ式関数型インターフェイスで処理を簡潔に書ける方法

この流れが分かると、GUIイベント処理は「ボタンを押したときだけ反応するもの」ではなく、「ユーザーの細かな動きに応じて画面を変えられる仕組み」として見えてきます。

Frame は画面の土台です。

Button はマウスイベントの発生元です。

MouseListener はマウスの動きを受け取る見張り係です。

mouseEntered と mouseExited は、マウスが入った、出たという変化に応じて動く処理です。

そして addMouseListener は、Button と MouseListener をつなぐ登録です。

鬼滅の刃風に言えば、鬼殺隊本部の修行案内盤に修行札を取り付け、その札と見張り係を連絡線でつなぎ、隊士の手が近づいたり離れたりした瞬間に、案内表示を切り替える。

この一連の流れが、マウスイベント処理の基本です。