java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

MouseAdapter

投稿日:

最終更新日:2018年01月16日

アイキャッチ

こんにちは。「Javaを復習する初心者」です。

JavaでGUIプログラミングをする場合、マウスイベントを扱うことがあると思います。今回はマウスをクリックを取得することと、マウスの動きを取得することについて、気が付いて点を書いてみました。

MouseAdapterを使う

MouseEventとprintln()

マウスイベントを利用する時、MouseAdapterクラスを利用します。このクラスには例えば、以下のメソッドがあります。

  • public void mouseClicked(MouseEvent e)

このメソッドの引数eをSystem.out.println()の引数に指定した場合、何が出力されるでしょうか。

Systemクラスにはoutというフィールドが定義されています。これはPrintStreamクラスです。このクラスにprintln()メソッドが定義されています。引数は色々使えます。そのうち、今回はMouseEvent eに対して、println(e)の出力が知りたいので、println(Object x)を使うことになります。ソースをEclipseで追っていった結果、e.toString()の結果が出力されることが分かりました。

MouseEvent#toString()

では、MouseEventのtoString()は何を出力するでしょうか。実際に出力してみた方が早いので、以下のソースを実行してみました。

ソース

public class MyJFrame extends JFrame {

	public static void main(String[] args) {
		new MyJFrame();
	}

	public MyJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setSize(400, 400);
		addMouseListener(new MyMouseAdapter());

		setVisible(true);
	}

	private class MyMouseAdapter extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent e) {
			System.out.println(e);
		}
	}
}

このプログラムでは、ウィンドウが表示され、領域をクリックするとコンソールに出力します。適当なところでクリックした結果、以下のように出力されました。

結果

java.awt.event.MouseEvent[MOUSE_CLICKED,(308,204),absolute(901,400),button=1,modifiers=Button1,clickCount=1] on frame0

Eclipseで調べると、AWTEventのtoString()メソッドが動作しているようです。さらに調べると、構成が一部だけわかりました。次のように出力されているようです。

  • e.getClass().getName()の結果 + [ e.paramString()の結果 ] + フレーム側のgetName()

最後の「フレーム側のgetName()」は出力結果からの判断です。MyJFrameのコンストラクタでthis.getName()を出力させると同じ結果が得られます。

paramString()

AWTEventクラスにparamString()メソッドが定義されています。仕様書によれば、このメソッドはデバッグ専用です。実装によって、結果が変わるそうです。

出力結果をみて今回判断できた箇所は以下です。

  • getX()とgetY()でクリックされた箇所の座標を出力
  • getXOnScreen()とgetYOnScreen()でクリックされた箇所のスクリーン上の座標を出力
  • getButton()で押されたマウスボタンがどれかを出力
  • getClickCount()で(おそらく短時間に)クリックされた回数を出力

getClickCount()は間隔をある程度置いてクリックしても回数が1に戻ってしまうようです。

getButton()の結果の数値は私の環境では左クリックで「1」、右クリックで「3」が出力されました。ホイール付きのマウスだかららしいです。ホイールでもクリックが出来て、「2」が出力されました。

リスナー

MouseAdapterクラス

java.awt.Componentクラスにはマウスのリスターについて、次の3つのメソッドが定義されています。

  • addMouseListener()
  • addMouseMotionListener()
  • addMouseWheelListener()

前述のプログラムでは、addMouseListener()を使いました。引数はインタフェースMouseListenerです。もし、MouseListenerの実装クラスを引数に使う場合は抽象メソッドを全て実装する必要があります。

Eclipseの機能で引数を保管した場合、次のようになります。

ソース

		addMouseListener(new MouseListener() {
			
			@Override
			public void mouseReleased(MouseEvent e) {
				// TODO 自動生成されたメソッド・スタブ
				
			}
			
			@Override
			public void mousePressed(MouseEvent e) {
				// TODO 自動生成されたメソッド・スタブ
				
			}
			
			@Override
			public void mouseExited(MouseEvent e) {
				// TODO 自動生成されたメソッド・スタブ
				
			}
			
			@Override
			public void mouseEntered(MouseEvent e) {
				// TODO 自動生成されたメソッド・スタブ
				
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
				// TODO 自動生成されたメソッド・スタブ
				
			}
		});

クリックのみを取得しないなら、クラスMouseAdapterを使った方がコードが短くて済みます。仕様書には、「このクラス内のメソッドは空です。」とあります。必要なメソッドだけオーバーライドすればよいことになります。具体的には、クリックだけ必要なばあいは、以下のようにMouseAdapterを拡張したクラスを作り、mouseClicked()メソッドだけ記述すればよいです。

ソース

	private class MyMouseAdapter extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent e) {
			// クリック時の処理
		}
	}

あとは、フラーム側で以下のように記述すればよいでしょう。

ソース

addMouseListener(new MyMouseAdapter());

implements

MyMouseAdapterのimplements文には、MouseListener, MouseWheelListener, MouseMotionListenerが記述されています。なので、マウスのクリックとマウスの動作を同じ拡張クラス内に記述することができます。具体的には以下のような記述になります。

ソース

	private class MyMouseAdapter extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent e) {
			// クリック時の処理
		}

		@Override
		public void mouseMoved(MouseEvent e) {
			// マウス移動時の処理
		}
	}

フレーム側では以下のように記述します。

ソース

		MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
		addMouseListener(myMouseAdapter);
		addMouseMotionListener(myMouseAdapter);

上記ではaddMouseListenerとaddMouseMotionListenerに同じオブジェクト(MyMouseAdapterのインスタンス)を指定しています。

サンプル

以下はmouseClicked()メソッドとmouseMoved()メソッドを使ったサンプルです。マウスポインタの位置に文字を表示しています。

ソース

public class MyJFrame extends JFrame {

    private final static String FORMAT = "(%d, %d)";
    private final static int WIDTH  = 300;
    private final static int HEIGHT = 300;


    private String message = null;
    private int x = 0;
    private int y = 0;

    public static void main(String[] args) {
        new MyJFrame();
    }

    public MyJFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setSize(WIDTH, HEIGHT);

        MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
        addMouseListener(myMouseAdapter);
        addMouseMotionListener(myMouseAdapter);

        setVisible(true);
    }

    public void paint(Graphics g) {
        // 画面を塗りつぶす
        g.setColor(Color.white);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        // メッセージがある場合、
        if (message != null) {
            // メッセージを描画する
            g.setColor(Color.red);
            g.drawString(message, x, y);
        }
    }

    private class MyMouseAdapter extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent e) {
            // クリック時の処理
            x = e.getX();
            y = e.getY();
            message = String.format(FORMAT, x, y) + "クリック";
            repaint();
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            // マウス移動時の処理
            x = e.getX();
            y = e.getY();
            message = String.format(FORMAT, x, y);
            // 描画する
            repaint();
        }
    }
}

マウスをフレームの上で動かすと、x座標y座標を取得して、そこに「(x, y)」と描画します。クリックすると「(x, y)クリック」と描画されます。

repaint()メソッドは内部クラスMyMouseAdapterの外側のクラスのメソッドです。これを明示的に記述する場合は、次のようにします。

ソース

			MyJFrame.this.repaint();

[外側のクラス].thisという書き方が出来ます。