Event Listeners and Registration
Mouse motion events are handled through the MouseMotionListener interface, which declares mouseDragged and mouseMoved.
Why a Program Listens to the Mouse
Every time the mouse moves, the computer produces mouse motion events. The operating system uses those events to redraw the pointer on screen, but a program can also observe the same events and react to them. The usual reason is to let the user drag something: pick up an object with the mouse, move it, and let go. This article follows how a program hooks into those motion events, which methods receive them, and how to trace the flow of a drag through the code.
Keep one idea in mind as you read: the program does not decide which kind of motion event it is receiving. The system makes that decision from the state of the mouse buttons at the moment the pointer moves. Your job as the programmer is to write handlers for each situation and to connect those handlers to the component you care about.
Two Handlers for One Movement
What do you think happens?
The user holds the left mouse button and moves the pointer across a window. Which handler does the program receive?
Reveal answer
mouseDragged, because a button is held down while the pointer moves
The deciding factor is whether any mouse button is held down while the pointer moves. A button held down produces a call to mouseDragged. This is the case the drag-and-drop pattern depends on.
The two cases are handled by two separate methods of one interface, MouseMotionListener. The interface declares mouseDragged and mouseMoved. If a mouse button is down when the pointer moves, the program receives a call to mouseDragged. If no button is down, the program receives a call to mouseMoved instead. The prediction you just made depends on noticing the button state, and that is the single fact that separates the two handlers.
Choosing Between mouseDragged and mouseMoved
The diagram shows the order of checks. The first and only deciding question is whether a button is held down at the moment of movement. The program does not run the check itself. By the time your handler is called, the system has already chosen the method that matches the button state.
A drag is not a single event. While the user keeps moving the mouse with a button held, the matching method is called once for every motion event. One drag therefore produces a long run of calls to mouseDragged. A handler must be written to run many times in quick succession, and it should not assume that one call equals one complete drag.
| Aspect | mouseDragged | mouseMoved |
|---|---|---|
| When it is called | The mouse moves while a button is pressed | The mouse moves with no button down |
| Typical use | Letting the user drag something across the screen | Reacting to the pointer when no button is pressed |
| How often it runs during one gesture | Once per motion event, so many times during a drag | Once per motion event while the pointer moves without a button |
| Who chooses it | The system, from the button state | The system, from the button state |
Comparing the two methods of MouseMotionListener
Implementing MouseMotionListener
Implementing the interface means writing a class that declares it and supplies both of its methods. Both methods take one parameter of type MouseEvent. That object carries the x and y coordinates of the pointer at the moment the event occurred. A class that implements the interface must provide both methods, even if it only cares about dragging. The parameter name evt is a convention. Inside a handler, you read the coordinates from that event object to learn where the pointer is during the drag.
- Both methods take a
MouseEventparameter named evt getX()andgetY()read the pointer location from the event objectmouseMovedmust still be present because the interface declares it
Registering the Listener on a Component
Implementing the interface is only half of the job. The listener object must also be connected to the component whose mouse motion it should hear. That connection is made by calling the component's addMouseMotionListener() method and passing in the listener object. Until this registration happens, the listener receives no motion events from that component. Once registered, it receives mouseDragged and mouseMoved events associated with that component, and a listener registered on one component does not hear motion over another.
The diagram shows that the component produces the motion events, but the listener is the object whose method runs. The system calls the handler on the listener, passing in a MouseEvent for the current movement. Your program does not call mouseDragged or mouseMoved directly during a drag.
- The class implements
MouseMotionListener addMouseMotionListener(this)registers this window as the listener- Without that call, neither handler receives motion events from this window
Tracing a Drag from Start to Finish
Tracing the handler calls for one gesture
The DragWindow above is running. The user presses the left button, moves the pointer through three positions while holding the button, releases the button, and then moves the pointer through two more positions. Which handler is called for each motion, and how many times in total?
- 1Press the button
Pressing a button is a mouse event handled by the
MouseListenerinterface, not byMouseMotionListener. It does not callmouseDraggedormouseMoved, because no motion has happened yet. - 2Move three times with the button held
Each motion event occurs while a button is down, so the system calls
mouseDraggedonce per motion event. That is three calls, and each one receives the pointer coordinates at that moment. - 3Release the button
Releasing the button is also not a motion event for this interface. No motion handler runs at the release itself.
- 4Move two times with no button down
Now no button is down, so the system calls
mouseMovedfor each motion event. That is two calls, andmouseDraggedis not called for them. - 5Total the calls
mouseDraggedruns three times andmouseMovedruns two times, for five motion handler calls in all.
mouseDragged is called 3 times during the held-button movement, and mouseMoved is called 2 times after release. The button-state check decides every call.
The sequence view makes the divergence point easy to find. A common wrong prediction is that every movement in the gesture calls mouseMoved, or that mouseDragged keeps running after the release. Locate the first step where your prediction and the actual handler differ. In the sequence above, that step is usually a move right after the release, or a move made while the button was still held.
Mistakes When Handling Mouse Motion
Common mistake Expecting one call to mouseDragged per drag
What it looks likeAssuming a handler runs once when the user starts dragging and again when they stop.
Why it failsWhile the user keeps moving with a button held, the matching method is called once for each motion event, which produces a long run of calls.
Do this insteadWrite the drag handler to run repeatedly and to tolerate that frequency.
Common mistake Implementing the interface but leaving out one method
What it looks likeA class declares implements MouseMotionListener and supplies only mouseDragged.
Why it failsThe interface declares both
mouseDraggedandmouseMoved, and a class that implements it must supply both, even if it only cares about dragging.Do this insteadProvide mouseMoved too, even as an empty method.
Common mistake Forgetting to register the listener
What it looks likeThe class implements the interface correctly, but addMouseMotionListener() is never called.
Why it failsA listener must be registered with a component's
addMouseMotionListener()method before it receives any motion events from that component.Do this insteadCall addMouseMotionListener() on the component, passing the listener object.
Common mistake Registering on the wrong component
What it looks likeRegistration is done on a panel, but the user is moving the mouse over a different window.
Why it failsA listener registered on one component does not hear motion over another.
Do this insteadRegister the listener on the component whose motion you want to observe.
Common mistake Treating mouseMoved as the handler for drags, when the button state decides the method
Keeping Motion Handling Separate
Mouse motion methods live in their own interface, separate from the other mouse events. The reason is volume. Motion events are generated continuously whenever the pointer moves, so the number is very large. 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 MouseListener events without automatically receiving every motion event as well.
In practice, a listener that handles dragging often also implements MouseListener so it can respond to the other mouse events. The two interfaces remain separate in the language, but a single object can implement both when its job requires it. The guiding rule is that a listener should implement only the interfaces its job needs.
Practice: Trace the Handler Calls
A DragWindow is registered with addMouseMotionListener(this). The user does the following in order: holds the button and moves four times, releases the button, moves two times, then presses again and moves once while holding. Write down which handler runs for each motion, and identify the first step where you would be most likely to predict wrongly.
Hints
- Ask first whether a button is down at the moment of each movement, not whether the user pressed a button at some earlier time.
- Pressing and releasing are not motion events for this interface, so they do not themselves trigger
mouseDraggedormouseMoved. - The step most often predicted wrongly is the first movement after a release.
Suppose the DragWindow registration line were removed, but the class still implements MouseMotionListener and has both methods. Predict whether a drag over the window would call mouseDragged, and explain which fact from the registration section decides your answer.
Hints
- Consider what the registration does for the component, not what the class declares.
- The source says a listener must be registered before it receives any motion events from that component.
Key Takeaways
- The system chooses between
mouseDraggedandmouseMovedbased on whether a mouse button is held down when the pointer moves, and the program does not make that choice itself. - A single drag produces many calls to
mouseDragged, one for each motion event, so drag handlers must be written to run repeatedly. - A class implementing
MouseMotionListenermust supply bothmouseDraggedandmouseMoved, and each receives aMouseEventcarrying the pointer coordinates. - A listener only receives motion events after it is registered with
addMouseMotionListener()on the specific component, and it hears nothing from other components. - When tracing a sequence of mouse actions, find the first step where the predicted handler differs from the actual one, since that divergence points to a button-state misjudgment or a missing registration.