MouseListener and Mouse Click Handling
Mouse motion events are handled through the MouseMotionListener interface, which declares mouseDragged and mouseMoved.
The Pointer Reports Its Own Movement
Every time the mouse moves, the computer produces mouse motion events. The operating system consumes these events to redraw the pointer on the screen, but a program can also observe the same events and react to them. The usual reason to do this is to let the user drag something: pick up an object with the mouse, move it, and let go. This article shows how a program hooks into those motion events, which methods receive them, and how to follow the flow of a drag through the code.
What do you think happens?
A program is listening for mouse motion on a panel. The user moves the mouse across the panel with no button pressed. Which method does the program receive?
Reveal answer
mouseMoved
mouseMoved is called when the mouse moves with no button down. Dragging is the case where a button is held during the movement, so a plain move with no button falls to mouseMoved.
Tracing the Button State
The whole mechanism depends on one fact: whether any mouse button is held down at the moment the pointer moves. That fact is tracked by the system, not by your program. Your code never decides which handler runs; it only receives the call. So the most useful way to reason about a mouse event is to ask two questions in order: what is the button state right now, and therefore which handler is about to be called?
The diagram below shows the button state moving between two conditions. Each pointer movement is answered by whichever handler matches the state at that moment. Pressing the button switches the state to held, and releasing it switches the state back to idle.
Notice that the state, not the movement itself, is what changes the handler. The same physical motion of the pointer produces mouseMoved while the button is up and mouseDragged while it is held. Keep this in mind when tracing: before each call, look up the state the button is in at that moment.
Choosing Between mouseDragged and mouseMoved
Mouse motion handling is organized around a single interface called MouseMotionListener. It declares two methods, and each covers a different situation. The deciding factor is whether any mouse button is held down while the pointer moves. If one is, the program receives a call to mouseDragged. If no button is down, the program receives a call to mouseMoved instead.
Both methods take one parameter of type MouseEvent. That object carries the x and y coordinates of the mouse's location at the moment the event occurred. A class that implements the interface must supply both methods, even if it only cares about dragging. The declarations have this shape:
- Two methods are declared; a class implementing the interface must provide both
- Each method takes one
MouseEventparameter - The parameter name evt is a convention; the coordinates are read from the event object inside the handler
The parameter name evt is only a convention in the declaration. Inside a handler, the coordinates are read from that event object, which is how the program learns where the pointer currently is during the drag.
Writing a Drag-Aware Listener
Here is a small listener that reacts to dragging. It implements both methods, as the interface requires. Its mouseDragged handler reads the coordinates from the event, while mouseMoved does nothing because this listener only cares about drags. This example is written for illustration; it is not taken from the source.
- The class implements the interface, so both methods must be present
mouseDraggedreads the pointer position from the event objectmouseMovedexists only to satisfy the interface
Registering the Listener on a Component
Implementing the interface is only half of the job. The listener must also be connected to the component whose mouse motion it should hear. This connection is made by calling the component's addMouseMotionListener() method and passing in the listener object. Until that call happens, the listener receives no motion events from that component at all.
- The listener object is created first
addMouseMotionListener()is called on the component whose motion should be observed- The listener only hears motion over canvas, not over any other component
Following a Full Mouse Sequence
Now consider a sequence of actions on a registered component: the button is pressed, the pointer moves, the button is released, and the pointer moves again. The diagram shows the calls the listener receives, in order. Before reading the diagram, try to predict which handler fires for each movement.
Tracing a Drag That Ends and Resumes Plain Movement
A listener is registered on a canvas. The user presses the button, moves the pointer across the canvas several times, releases the button, and then moves the pointer once more. Which handler runs for each movement?
- 1Identify the button state at the first movement
The button was pressed before the move, so it is held. The system chooses
mouseDragged. - 2Repeat for each movement while held
Each motion event while the button stays down produces another
mouseDraggedcall. A drag is a long run of such calls, so the handler must tolerate repeated calls. - 3Check the state after release
Releasing the button returns the state to up. The next movement has no button down, so the system calls
mouseMovedinstead. - 4Compare with a prediction
A common wrong prediction is that the final move still runs
mouseDraggedbecause the drag was 'in progress'. The divergence is at the release step: the state was reset, so the handler changed.
Movements while the button is held call mouseDragged, repeatedly. The movement after release calls mouseMoved.
Mistakes That Make Predictions Diverge
Common mistake Expecting mouseDragged to run once per drag
What it looks likeAssuming a single drag across the canvas triggers one call to mouseDragged.
Why it failsMotion events are generated continuously while the pointer moves, so a drag produces a long series of calls.
Do this insteadWrite the drag handler so it can run many times in a row, for example by updating a position on each call rather than doing one-time work.
Common mistake Forgetting that the button state decides the handler
What it looks likePredicting mouseDragged for every move during a session because the program 'is doing dragging'.
Why it failsThe choice is made by the system based on the button state at the moment of movement, not by the program's purpose.
Do this insteadBefore each motion call, check whether the button is down at that moment.
Common mistake Implementing only one method
Why it failsA class implementing
MouseMotionListenermust supply bothmouseDraggedandmouseMoved, even if one is empty.Do this insteadProvide both methods, leaving mouseMoved with no body if plain movement is not needed.
Common mistake Assuming registration is global
What it looks likeRegistering on one panel, then expecting motion over a different component to call the handler.
Why it failsA listener registered on one component does not hear motion over another.
Do this insteadCall addMouseMotionListener() on the exact component where motion should be observed.
Why Motion Methods Live in Their Own Interface
Mouse motion handlers are declared in a separate interface from the other mouse events. The reason is volume. Motion events are generated whenever the pointer moves, which means a very large number of them. A program that only cares about clicks would be inefficient if it were forced to hear every movement. Keeping the motion methods in MouseMotionListener lets a program listen for other mouse events without automatically receiving all motion events too.
The two interfaces are separate in the language, but a single object can implement both when its job requires it. A listener that handles dragging often also implements MouseListener so that it can respond to the other mouse events as well.
Implement only the interfaces a listener's job needs. A class that only tracks drags should implement MouseMotionListener, and should add MouseListener only when it must respond to the other mouse events.
Trace a Drag Sequence
A listener is registered on a panel. The user performs these actions in order: press the button, move the pointer, move the pointer again, release the button, move the pointer. Write down the handler called for each movement, then identify the first step where a prediction based only on 'the user moved the mouse' would be wrong.
Hints
- Ask the button state question before each movement: is a button down at that instant?
- Only the movements while the button is held should produce
mouseDragged. - The first divergence usually happens right after the button is released.
Key Takeaways
- A mouse motion event leads to
mouseDraggedwhen a button is held down during the movement and tomouseMovedwhen no button is down; the system makes this choice from the button state. MouseMotionListenerdeclares bothmouseDraggedandmouseMoved, each taking aMouseEvent, so an implementing class must provide both methods.- A listener receives motion events only after it is registered with the component's
addMouseMotionListener()method, and it hears only that component. - A drag produces a long run of repeated calls, so drag handlers must tolerate frequent invocation.
- When a trace diverges from prediction, check the button state at that step first, then check whether the listener was registered on the component being watched.