0

I have a circle being repainted continually to show animation. I would like to have the circle flash different colors if clicked. When I tried implementing MouseListener to get a mouseClicked event, it did not work. I believe that is due to the constant repainting. Is there another way to have this circle bounce around and still catch a mouse click? I added a KeyEvent to test, and it worked fine. There is no "main" as this was called from another program.

import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Random;
import java.util.Timer;

public class Catch extends Canvas {

    int xCor, yCor, xMove, yMove;
    Color currentColor;
    Random ranNumber;
    boolean flashing = false;

    public Catch() {
        enableEvents(java.awt.AWTEvent.KEY_EVENT_MASK);
        requestFocus();
        xCor = 500;
        yCor = 350;
        xMove = 5;
        yMove = 5;
        currentColor = Color.black;
        ranNumber = new Random();
        Timer t = new Timer(true);
        t.schedule(new java.util.TimerTask() {
            public void run() {
                animate();
                repaint();
            }
        }, 10, 10);

    }

    public void paint(Graphics g) {
        g.setColor(currentColor);
        g.fillOval(xCor, yCor, 20, 20);
    }

    public void processKeyEvent(KeyEvent e) {
        if (e.getID() == KeyEvent.KEY_PRESSED) {
            if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                flashing = !flashing;
            }
        }
    }

    public void animate() {
        xCor += xMove;
        yCor += yMove;

        // and bounce if we hit a wall
        if (xCor < 0 || xCor + 20 > 1000) {
            xMove = -xMove;
        }
        if (yCor < 0 || yCor + 20 > 700) {
            yMove = -yMove;
        }

        if (flashing) {
            int r = ranNumber.nextInt(256);
            int g = ranNumber.nextInt(256);
            int b = ranNumber.nextInt(256);
            currentColor = new Color(r, g, b);
        }
    }

    public boolean isFocusable() {
        return true;
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    [How to Write a Mouse Listener](http://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html)? Any reason you're using `Canvas`? I only ask, because the main reason to use `java.awt.Canvas` is to get use a `BufferStrategy`, otherwise you should probably be using a `JPanel` and get double buffering for free – MadProgrammer Apr 11 '16 at 02:02
  • Also, you event handling is really, really dated – MadProgrammer Apr 11 '16 at 02:03
  • You basic problem could also be a race condition between the timer and the event dispatching thread, so by the time the time you've been notified about the mouse event, it's position has already been updated. May be using a Swing `Timer` or synchronized block around the critical areas might help – MadProgrammer Apr 11 '16 at 02:07
  • I guess the only reason I am using `Canvas` is that is what I was taught to use when "painting". Any of my code being "dated" does not surprise me. I have been out of coding for a while. I am, however, happy to learn better and more current ways to do things. I have not used a synchronized block before. I will do some research there. – AnotherJones Apr 12 '16 at 01:36

1 Answers1

1

Your approach is a little out of date, we don't tend to use enableEvents any more, but instead make use of a number of different "observers" which provide notification about certain events.

I'd start by having a look at Painting in AWT and Swing and Performing Custom Painting and How to Write a Mouse Listener

I'd also avoid using KeyListener and instead use the Key Bindings API which was designed to overcome many of the shortcommings of KeyListener.

While cutting edge would be to use JavaFX, if you have a knowledge of AWT, then stepping up to Swing would simpler, for example:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Ball ball;

        public TestPane() {
            ball = new Ball();
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ball.update(getSize());
                    repaint();
                }
            });
            timer.start();

            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    ball.setHighlighted(ball.wasClicked(e.getPoint()));
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            ball.paint(g2d);
            g2d.dispose();
        }

    }

    public class Ball {

        private int radius = 10;
        private int xDelta, yDelta;
        private Ellipse2D shape = new Ellipse2D.Double(0, 0, radius * 2, radius * 2);
        private boolean highlighted;
        private int cycleCount;

        public Ball() {
            Random rnd = new Random();
            xDelta = rnd.nextInt(3) + 1;
            yDelta = rnd.nextInt(3) + 1;
        }

        public void update(Dimension bounds) {
            Rectangle shapeBounds = shape.getBounds();
            shapeBounds.x += xDelta;
            shapeBounds.y += yDelta;
            if (shapeBounds.x + shapeBounds.width > bounds.width) {
                shapeBounds.x = bounds.width - shapeBounds.width;
                xDelta *= -1;
            } else if (shapeBounds.x < 0) {
                shapeBounds.x = 0;
                xDelta *= -1;
            }
            if (shapeBounds.y + shapeBounds.height > bounds.height) {
                shapeBounds.y = bounds.height - shapeBounds.height;
                yDelta *= -1;
            } else if (shapeBounds.y < 0) {
                shapeBounds.y = 0;
                yDelta *= -1;
            }
            shape.setFrame(shapeBounds);

            if (highlighted) {
                cycleCount++;
                if (cycleCount > 12) {
                    highlighted = false;
                }
            }
        }

        public boolean wasClicked(Point p) {
            return shape.contains(p);
        }

        public void setHighlighted(boolean value) {
            highlighted = value;
            cycleCount = 0;
        }

        public void paint(Graphics2D g) {
            if (highlighted) {
                g.setColor(Color.RED);
            } else {
                g.setColor(Color.BLUE);
            }
            g.fill(shape);
        }
    }

}

You should also have a look at How to use Swing Timers

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • The swing timer definitely helped with the mouse event, and I am incorporating that into the project. Thanks! – AnotherJones Apr 14 '16 at 02:25
  • Thank you for taking the time to write out that sample code. There are several things in there I have not used before, so I am going through it line by line to make sure I grasp what is going on. Having the sample in addition to the resource links have helped...and I am getting it. However, after reading through the key bindings material, I do not feel any closer than I was before. I believe you that `KeyListener` is outdated, but it seems a lot easier to understand. I looked up several articles about Key Bindings, but it just isn't clicking with my brain. – AnotherJones Apr 14 '16 at 02:25
  • Key bindings can take some time to grasp, but they represent apart of large re-usable code element, `Action`s, so you could use the same `Action` for a menu item, button and/or key binding, making it extremely useful – MadProgrammer Apr 14 '16 at 02:30
  • For [example](http://stackoverflow.com/questions/20873255/keylistener-vs-key-bindings/20873354#20873354) and there are countless exampes on SO – MadProgrammer Apr 14 '16 at 02:41