Mouse Events IntroductionInteractive input in graphical applications is often accomplished
with a pointing device such as a mouse or trackball.
Such applications react to mouse button clicks, mouse movement, and mouse dragging
(i.e., moving the mouse when a button is held down).
BreezyGUI includes several methods that respond to mouse events. Each method has two parameters:
BreezyGUI's mouse handling methods are
listed in the following table:
To detect and handle mouse events, an application must implement one
or more of the above methods. A mouse
handling method typically begins by transferring the values of the mouse coordinates to
application instance variables. The method
then usually invokes the repaint
method, which in turn performs some actions and updates the display. For example, consider the following mousePressed
method: public void mousePressed (int
x, int y){ mouseX = x; mouseY = y; repaint(); } Example 1: A Primitive Sketching ProgramAs a first example, we present a program that allows a user to make a
very primitive type of drawing. Each time the
user depresses a mouse button, the program draws a pellet-sized dot at the mouse's current
location, with results similar to the following:
The
drawing is supposed to look like a happy face. Here
is the code: import java.awt.*; import BreezyGUI.*; public class Sketchpad1 extends GBFrame{
public Sketchpad1(){
setTitle ("Pellet Drawing");
} public void mousePressed (int x, int y){ Graphics g = getGraphics(); g.fillOval
(x, y, 5, 5); } public static void main (String[] args){ Frame frm = new Sketchpad1(); frm.setSize (200, 200); frm.setVisible (true); } } The Transient Image Problem and the Sketching ProgramIn addition to being primitive, the sketching program has a major
problem. When the application window is
resized or covered and then uncovered, all the dots disappear. The reason is that the
application draws a transient image. When the
window is resized, Java invokes repaint,
which invokes update,
which erases any images drawn by the program. To draw a permanent or refreshable image, one that reappears when the
window is resized, the application must maintain a record of the image in its instance
variables and redraw the image when necessary. We
now modify the application so that it maintains a list of pellet positions in two parallel
arrays. In addition:
The application handles the details of saving and displaying points
in two new methods,
savePoint(int
x, int y) and displayPoints(Graphics
g). Here is the listing:
import java.awt.*; import BreezyGUI.*; public class Sketchpad2 extends GBFrame{ private static int MAX_POINTS = 500; private int numPoints; private int xArray[]; private int yArray[]; public Sketchpad2(){ setTitle ("Pellet Drawing"); numPoints = 0; xArray = new int[MAX_POINTS]; yArray = new int[MAX_POINTS]; } public void paint (Graphics g){ displayPoints (g); } public void mousePressed (int x, int y){ if (numPoints < xArray.length){ Graphics g = getGraphics(); g.fillOval (x, y, 5, 5); savePoint (x, y); } else messageBox ("Sorry: cannot draw another pellet."); } private void displayPoints (Graphics g){ int i; for (i = 0; i < numPoints; i++){ g.fillOval (xArray[i], yArray[i], 5, 5); } } private void savePoint (int x, int y){ xArray[numPoints] = x; yArray[numPoints] = y; numPoints++; } public static void main (String[] args){ Frame frm = new Sketchpad2(); frm.setSize (200, 200); frm.setVisible (true); } } Improving the Sketching ProgramThere are several simple but worthwhile modifications that can be
made to our still very primitive drawing program.
Example 2: Dragging Circle ObjectsAs a second example of a mouse enabled program, we consider an
application that begins by drawing several circles of random color and size in a window. The user can then use the mouse to drag these
circles around the window. Here is what the
interface looks like before and after some circles have been dragged around:
This application uses two classes, one to define a circle and the
other to define the user interface. Here is
the code: import java.awt.*; public class Circle{ private int centerX, centerY, radius; private Color color; public Circle(int x, int y, int r, Color c){ centerX = x; centerY = y; radius = r; color = c; } public int getCenterX(){ return centerX; } public int getCenterY(){ return centerY; } public int getRadius(){ return radius; } public Color getColor(){ return color; } public void setCenterX(int x){ centerX = x; } public void setCenterY(int y){ centerY = y; } public void setRadius(int r){ radius = r; } public void setColor(Color c){ color = c; } public void draw(Graphics g){ Color oldColor = g.getColor(); g.setColor(color); // Translates circle's center to rectangle's origin for drawing. g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2); g.setColor(oldColor); } public boolean containsPoint(int x, int y){ int xSquared = (x - centerX) * (x - centerX); int ySquared = (y - centerY) * (y - centerY); int radiusSquared = radius * radius; return xSquared + ySquared - radiusSquared <= 0; } public void move(int xAmount, int yAmount){ centerX = centerX + xAmount; centerY = centerY + yAmount; } } import java.awt.*; import BreezyGUI.*; public class DragCircles1 extends GBFrame{ private final static int MAX_CIRCLES = 5; private final static int MIN_CENTER_X = 50; private final static int MAX_CENTER_X = 150; private final static int MIN_CENTER_Y = 50; private final static int MAX_CENTER_Y = 150; private final static int MIN_RADIUS = 10; private final static int MAX_RADIUS = 50; private int mouseX = 0, mouseY = 0; private boolean circleHasBeenSelected = true; private int currentX = 0; private int currentY = 0; private Circle selectedCircle; private Circle circles[] = new Circle[MAX_CIRCLES]; public DragCircles1(){ int i; for (i = 0; i < MAX_CIRCLES; i++){ int centerX = randomInt(MIN_CENTER_X, MAX_CENTER_X); int centerY = randomInt(MIN_CENTER_Y, MAX_CENTER_Y); int radius = randomInt(MIN_RADIUS, MAX_RADIUS); Color color = randomColor(); Circle circle = new Circle(centerX, centerY, radius, color); circles[i] = circle; } setTitle("Dragging Circles"); } public void paint (Graphics g){ int i; for (i = 0; i < MAX_CIRCLES; i++) circles[i].draw(g); } public void mousePressed(int x, int y){ currentX = x; currentY = y; circleHasBeenSelected = findCircle(); } public void mouseReleased(int x, int y){ circleHasBeenSelected = false; } public void mouseDragged(int x, int y){ if (circleHasBeenSelected){ selectedCircle.setCenterX( selectedCircle.getCenterX() - (currentX - x)); selectedCircle.setCenterY( selectedCircle.getCenterY() - (currentY - y)); repaint(); currentX = x; currentY = y; } } private boolean findCircle(){ int i; for (i = 0; i < MAX_CIRCLES; i++) if (circles[i].containsPoint(currentX, currentY)){ selectedCircle = circles[i]; return true; } return false; } private int randomInt(int low, int high){ return (int) (low + Math.random() * (high - low + 1)); } private Color randomColor(){ Color color; int number = randomInt(1, 5); switch (number){ case 1: color = Color.red; break; case 2: color = Color.blue; break; case 3: color = Color.green; break; case 4: color = Color.magenta; break; case 5: color = Color.cyan; break; default: color = Color.orange; } return color; } public static void main (String[] args){ Frame frm = new DragCircles1(); frm.setSize (300, 200); frm.setVisible(true); } } Getting Rid of FlickerThe above program has a problem.
As we drag a circle around, the window seems to flicker. The cause lies in the mouseDragged
method. Every time we move a circle, the
program calls the repaint
method. This method clears the window and
then calls paint
to redraw the circles. Unless the computer is
very fast, the human eye experiences the cycles of clear and redraw as flicker. Fortunately, there is a way to overcome flicker. Java allows drawing to be done in two different
modes. In the default mode, which we have
been using exclusively so far, images (lines, text, shapes) overwrite whatever happens to
be underneath them. This mode is called paint. The second mode is called XOR. The result of drawing an image in XOR mode depends
on two colors -- the current color and the XOR color.
Wherever the image overlays the XOR color it is drawn in the current color, and
vice-versa. A consequence of this strange
convention is that if an image is redrawn on top of itself, it disappears, and the window
returns to its original appearance before the image was first drawn. Pixels of a different color (neither the current
color nor the XOR color) underneath the image are changed in a manner too difficult to
explain here; however, redrawing over these pixels returns them to their original color
too. In the code that follows, we use XOR to solve the flicker problem.
When we select a circle, we draw an outline of it in XOR mode. Then, as the user drags the mouse, we redraw the
outline using XOR (restoring the pixels covered by the outline) and draw another outline
in XOR mode at the mouse's new location. Drawing
an outline involves so few pixels that the user sees no flicker and believes the outline
is smoothly following the mouse around the window. When
the user finally releases the mouse, we repaint the window.
Here are the required modifications to the program: public void mousePressed(int x, int y){ currentX = x; currentY = y; circleHasBeenSelected = findCircle(); if (circleHasBeenSelected) selectedCircle.drawOutline (getGraphics()); } public void mouseReleased(int x, int y){ circleHasBeenSelected = false; repaint(); } public void mouseDragged(int x, int y){ if (circleHasBeenSelected){ Graphics g = getGraphics(); selectedCircle.drawOutline (g); // Old location selectedCircle.move(x - currentX, y - currentY); selectedCircle.drawOutline (g); // New location currentX = x; currentY = y; } } The drawOutline
method in class Circle
looks like this: public void drawOutline (Graphics g){ Color oldColor = g.getColor(); g.setColor (Color.black); g.setXORMode (Color.white); g.drawOval (centerX - radius, centerY - radius, radius * 2, radius * 2); g.setColor (oldColor); g.setPaintMode(); } |
| ||||