Cannot make notify() to work with Bouncing Balls using Threads - java

I am trying to use one thread to control bouncing balls. I can add balls, delete balls, suspend the movement but when i try to resume the movement of bouncing balls, notify()/notifyAll() is not working. I want only one thread to control the movement of balls that are being added in a List. I would appreciate a simple explanation as I am a complete novice. Here is the code:
************************************************
public class BounceBallApp extends JApplet {
public BounceBallApp() {
add(new BallControl());
}
public static void main(String[] args) {
BounceBallApp applet = new BounceBallApp();
JFrame frame = new JFrame();
frame.add(applet); //new line added as per the reference
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Assig_1_Base");
frame.add(applet, BorderLayout.CENTER);
frame.setSize(400, 320);
frame.setVisible(true);
}
}
***************************************************************
public class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JScrollBar jsbDelay = new JScrollBar();
private JButton jbtAddBall = new JButton("+1");
private JButton jbtDeleteBall = new JButton("-1");
public BallControl() {
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
panel.add(jbtAddBall);
panel.add(jbtDeleteBall);
ballPanel.add();
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
jbtSuspend.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.suspend();
}
});
jbtResume.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.resume();
}
});
jbtAddBall.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.add();
}
});
jbtDeleteBall.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.delete();
}
});
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
}
public class BallPanel extends JPanel {
private int delay = 10;
private List<Ball> ballsArray = Collections.synchronizedList(new ArrayList());
//protected List<Ball> ballsArray = new ArrayList<>();
private int radius = 5;
boolean threadSuspended = false;
public BallPanel() {
start();
}
protected void start(){
Thread t;
t = new Thread(){
#Override
public void run(){
System.out.println("*************");
while (true){
repaint();
try {
Thread.sleep(delay);
synchronized(this){
if (threadSuspended==true) {wait();}
}
} catch (InterruptedException e){
e.getMessage();
}
}
}
};
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Ball ball : ballsArray){
g.setColor(ball.color);
if (ball.x < 0 || ball.x > getWidth())
ball.dx *= -1;
if (ball.y < 0 || ball.y > getHeight())
ball.dy *= -1;
ball.x += ball.dx;
ball.y += ball.dy;
g.fillOval(ball.x - radius, ball.y - radius, radius * 2, radius * 2);
}
}
public synchronized void suspend() {
threadSuspended = true;
}
public synchronized void resume() {
threadSuspended = false;
notify();
}
public void setDelay(int delay) {
this.delay = delay;
}
public void add(){
if (threadSuspended==false) ballsArray.add(new Ball());
}
public void delete(){
if (ballsArray.size() > 0 && threadSuspended==false)
ballsArray.remove(ballsArray.size() - 1); // Remove the last ball
}
}
**************************************************
public class Ball {
int x = 0;
int y = 0;
int dx = 2;
int dy = 2;
Color color = new Color(random(255),random(255),random(255));
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
}

You have a "context" issue...
You declare the Thread within the context of the BallPane...
So, when you call wait(), you are actually calling wait() on t
Thread t;
t = new Thread(){
#Override
public void run(){
/*...*/
// this = t
synchronized(this){
// This is the same as saying
// this.wait() or t.wait();
if (threadSuspended==true) {wait();}
}
}
};
But when you call notify, you are calling BallPane's notify method...
public synchronized void resume() {
threadSuspended = false;
// This is the same as this.notify or BallPane.this.notify()
notify();
}
So, t is waiting on t's monitor lock and you call BallPane's notify on it's monitor lock...meaning t will never be notified.
It would be better if you used a shared lock instead...
public class BallPanel extends JPanel {
protected static final Object SUSPEND_LOCK = new Object();
/*...*/
Thread t;
t = new Thread(){
#Override
public void run(){
/*...*/
synchronized(SUSPEND_LOCK ){
if (threadSuspended==true) {SUSPEND_LOCK.wait();}
}
}
};
/*...*/
public void resume() {
synchronized(SUSPEND_LOCK){
threadSuspended = false;
SUSPEND_LOCK.notify();
}
}

Related

keyPressed() and keyReleased() not being called KeyListener

I am trying to create a LinkedList of all the keys pressed, but the keyPressed() and keyReleased() methods aren't being called. I've already tried frame.setFocusable(true).
Here is my code (also sorry if's a big pile of spaghetti):
Window.java
public class Window {
protected JFrame frame;
private boolean clicked = false; // TODO: Mouse input
private LinkedList<Character> keysDown = new LinkedList<>();
public Window(String title, Dimension resolution, Canvas display) {
frame = new JFrame(title);
Dimension decoratedResolution = new Dimension(resolution.width + 16, resolution.height + 38); // The resolution of the window is mismatched, this makes it so resolution.height is actually the bottom of the screen and not a little further below and the same for resolution.width.
frame.setPreferredSize(decoratedResolution);
frame.setMinimumSize(decoratedResolution);
frame.setMaximumSize(decoratedResolution);
frame.setResizable(false);
frame.setLocationRelativeTo(null); // Center the window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
char character = e.getKeyChar();
if (!keysDown.contains(character))
keysDown.add(character);
}
#Override
public void keyReleased(KeyEvent e) {
char character = e.getKeyChar();
keysDown.remove((Object)character);
}
});
frame.add(display);
frame.setVisible(true);
}
public boolean isMouseDown() {
return clicked;
}
public Vector2 getMousePosition() {
Point point = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(point, frame);
return new Vector2(point);
}
public LinkedList<Character> getKeysDown() {
return keysDown;
}
}
Edit: All the other code relevant is below
P.S. This is one of my first times using stack overflow so If I made a mistake in formatting the question, let me know.
Game.java
public class Game extends Canvas implements Runnable {
// General Settings
public static final String TITLE = "Game";
public static final Dimension RESOLUTION = new Dimension(800, 450);
public static final int FPS = 60;
// Do not modify
protected static final long NANO_SECOND = 1000000000;
public float timeScale = 1f;
public volatile boolean running = false; // Set to false to stop the game
public Thread thread;
public Window window;
public Renderer renderer;
public static void main(String[] args) {
new Game();
}
public Game() {
start();
}
public void start() {
init();
}
public void init() {
window = new Window(TITLE, RESOLUTION, this);
}
public void run() {
final float RENDER_INTERVAL = 1f / FPS;
long then = System.nanoTime();
long now = then;
float deltaTime = 0f;
int frames = 0;
while (running) {
now = System.nanoTime();
deltaTime = (float)(now - then) / NANO_SECOND;
update(deltaTime * timeScale);
if (!running)
break;
then = now;
Thread.onSpinWait();
}
}
public void update(float deltaTime) {
if (window.getKeysDown().contains('d'))
System.out.println("d pressed");
}
It turns out I had to call addKeyListener() in the Game class.

How can I control thread(control ball speed)?

I have a question.
I'm studying java.
In this code, I want to slower/faster the ball that created after I clicked "slower/faster" Menu button.
I mean, when I added ball and when the ball I added is moving, and then I click slow or fast button, and the ball that was moving has the same speed, and the ball that created after I clicked button, that ball's speed have to be changed.
I tried lock and unlock method in BallRunnable class, but it doesn't work.
Could you give me an advice? Thanks :)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
import java.util.List;
import java.util.concurrent.locks.*;
public class BallBounce {
public static void main(String[] args) {
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
A runnable that animates a bouncing ball.
*/
class BallRunnable implements Runnable {
public BallRunnable(Ball aBall, JPanel ballPanel) {
ball = aBall; this.ballPanel = ballPanel;
ballLock = new ReentrantLock();
}
public void run() {
ballLock.lock();
try {
for (int i = 1; i <= STEPS; i++) {
ball.move(ballPanel.getBounds()); // update the location of the ball
ballPanel.paint(ballPanel.getGraphics());
Thread.sleep((long)(DELAY * change));
}
} catch (InterruptedException e) { System.out.println("what"); }
finally { ballLock.unlock(); }
}
public static void print() { System.out.println(DELAY * change); }
public static void setdoublechange() {
change = change * 2;
}
public static void sethalfchange() {
change = change / 2;
}
private Ball ball;
private JPanel ballPanel;
public static final int STEPS = 1000;
public static final int DELAY = 3;
private static double change = 1;
private static Lock ballLock;
}
class BallRunnable2 implements Runnable {
public BallRunnable2(Ball aBall, JPanel ballPanel) {
ball = aBall; this.ballPanel = ballPanel;
}
public void run() {
ballLock.lock();
try {
Thread.sleep(100);
for (int i = 1; i <= STEPS; i++) {
ball.move(ballPanel.getBounds()); // update the location of the ball
ballPanel.paint(ballPanel.getGraphics());
Thread.sleep(DELAY * change);
}
} catch (InterruptedException e) { }
finally { ballLock.unlock(); }
}
public static void setdoublechange() {
change = change * 2;
}
public static void sethalfchange() {
change = change / 2;
}
private Ball ball;
private JPanel ballPanel;
public static final int STEPS = 1000;
public static final int DELAY = 3;
public static int change = 1;
private Lock ballLock;
}
/**
A ball that moves and bounces off the edges of a rectangle
*/
class Ball {
/**
Moves the ball to the next position, reversing direction if it hits one of the edges
*/
public void move(Rectangle2D bounds) { // java.awt.geom.Rectangle2D
x += dx; y += dy;
if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; }
if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx= -dx; }
if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; }
if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; }
}
/**
Gets the shape of the ball at its current position.
*/
public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); }
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;
private double dy = 1;
}
/**
The panel that draws the balls.
*/
class BallPanel extends JPanel {
/**
Add a ball to the panel.
#param b the ball to add
*/
public void add(Ball b) {
balls.add(b);
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Ball b : balls) { g2.fill(b.getShape()); }
}
private List<Ball> balls = new ArrayList<>();
}
class BounceFrame extends JFrame {
public BounceFrame() {
setTitle("BounceThread");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ballPanel = new BallPanel(); add(ballPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Add 1", new ActionListener() {
public void actionPerformed(ActionEvent event) { addBall1(); }
});
addButton(buttonPanel, "Add 2", new ActionListener() {
public void actionPerformed(ActionEvent event) { addBall2(); }
});
addButton(buttonPanel, "Close", new ActionListener() {
public void actionPerformed(ActionEvent event) { System.exit(0); }
});
// addButton(buttonPanel, "Close", (ActionEvent event) -> System.exit(0));
add(buttonPanel, BorderLayout.SOUTH);
JMenu speedMenu = new JMenu("Speed");
JMenuItem fasterItem = speedMenu.add(new fasterAction("Faster"));
JMenuItem slowerItem = speedMenu.add(new slowerAction("Slower"));
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
menuBar.add(speedMenu);
}
private void addButton(Container container, String title, ActionListener listener) {
JButton button = new JButton(title);
container.add(button);
button.addActionListener(listener);
}
/**
Adds a bouncing ball to the canvas and starts a thread to make it bounce
*/
public void addBall1() {
Ball b = new Ball();
ballPanel.add(b);
Runnable r = new BallRunnable(b, ballPanel);
Thread t = new Thread(r);
t.start();
}
public void addBall2() {
Ball b1 = new Ball();
Ball b2 = new Ball();
ballPanel.add(b1);
ballPanel.add(b2);
Runnable r1 = new BallRunnable(b1, ballPanel);
Runnable r2 = new BallRunnable2(b2, ballPanel);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
private BallPanel ballPanel;
public static final int DEFAULT_WIDTH = 450;
public static final int DEFAULT_HEIGHT = 350;
}
class fasterAction extends AbstractAction {
public fasterAction(String name) { super(name); }
public void actionPerformed(ActionEvent event) {
BallRunnable.sethalfchange();
BallRunnable2.sethalfchange();
}
}
class slowerAction extends AbstractAction {
public slowerAction(String name) { super(name); }
public void actionPerformed(ActionEvent event) {
BallRunnable.setdoublechange();
BallRunnable2.setdoublechange();
}
}
Increase Thread.sleep() value (it is in milliseconds)
Changing dx and/or dy of the ball (when a certain condition is met).
If I am correct <dx, dy> is the velocity of the ball.

event dispatch thread in Java SWING program

I am totally a newbie in Java. And I want to create an animation. But I did not succeed. mouseClicked() will be executed in the event dispatching thread. Why doesn't it work? Will the event dispatching be occupied by other threads, what other threads?
public class DemoThreadGUI {
public static void main(String [] args) {
DemoThreadGUI app = new DemoThreadGUI();
app.go();
}
public void go() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
generateGUI();
}
});
}
public void generateGUI() {
JFrame frame = new JFrame("Demo");
frame.add(new MyPanel());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class MyPanel extends JPanel implements MouseListener {
private int x,y;
private int r;
public MyPanel() {
this.setPreferredSize(new Dimension(100,100));
this.addMouseListener(this);
x = 50;
y = 50;
r = 25;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x-r,y-r,r*2,r*2);
}
public void mouseClicked(MouseEvent event) {
int targetX = event.getX();
int targetY = event.getY();
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}
}
In your mouseClicked() start a new Thread and place the code in the thread
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
The repaint() call should be wrapped into SwingUtilities.invokeAndWait() to pass control to EDT
As Holger said, You can’t expect a thread to paint your UI when you told it to sleep.
Use this instead:
Make targetX and targetY global variables, and make a new java.util.Thread object t.
private int x,y;
private int r;
Thread t;
int targetX, targetY;
modify your mouseClicked method as:
public void mouseClicked(final MouseEvent event) {
targetX = event.getX();
targetY = event.getY();
t = new Thread(new Runnable(){public void run(){anim();}});
t.start();
}
Put your code of the mouseClicked method into the anim method as:
public void anim()
{
for(int i=0;i<10;++i) {
try{
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
Thread.sleep(100);
}catch(Exception e){}
}
t.stop();
}
This works perfectly.

Button event to animation

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 30,vy=20;
public int width = 500,height = 500;
public int diameter=100;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(10, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
timer2.start();
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start the animation");
button.revalidate();
}
else
{
isRunning = true;
MovingBall();
button.setText("Click me to stop the animation");
button.revalidate();
}
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
I am trying to construct a one button which handles two events mainly to start and stop the animation. What I am trying to do is when the user clicks the button the animation of the bouncing ball will start, and the text of the button will change to "Click me to stop", and when the user clicks the button again the animation will stop. I am using the timer.
The animation is okay, I worked that out, and that when the user clicks the button, the animation start that's okay too. The only problem I've got is how am I going to handle another event to the same button?
You could do something similar to this. This way you only need one Action Listener and a boolean to tell it which action to do.
boolean isRunning = false
button.addActionListener(new YourActionListener());
public class YourActionListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start");
button.revalidate();
}
else
{
isRunning = true;
button.setText("Click me to stop");
button.revalidate();
}
}
}
}
Edit
Your code now works. What you wanted to do was call the MoveBall method when timer2 fired the Action Listener.
This can be done by
if(e.getSource()==timer2)
{
MovingBall();
}
so in your code it would look like.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 10,vy=5;
public int width = 500,height = 500;
public int diameter=50;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(25, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(timer2.isRunning())
{
button.setText("Click me to start the animation");
button.revalidate();
timer2.stop();
}
else
{
button.setText("Click me to stop the animation");
button.revalidate();
timer2.start();
}
}
if(e.getSource()==timer2)
{
MovingBall();
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
You can remove the previous ActionListener on the JButton using button.removeActionListener(<your first action listener>) and then add the second action listener using button.addActionListener(<your second action listener>). Then, to change the text on the button, just use button.setText("Your Text Here").

Cannot find symbol error. java

I am running the following code and i am getting a cannot find symbol error at resultPane.setResult(0); am i putting the new handlerclass in wrong spot? ive tried putting
it in different spots but nothing
public class MovingLabel {
public static void main(String[] args) {
new MovingLabel();
}
public MovingLabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
ResultPane resultPane = new ResultPane();
JFrame frame = new JFrame("Testing");
frame.setGlassPane(resultPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new NewPane(resultPane));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setBounds(500,300,200,200);
}
});
}
public class ResultPane extends JPanel {
private JLabel result;
private Timer timer;
private int xDelta = (Math.random() > 0.5) ? 1 : -1;
private int yDelta = (Math.random() > 0.5) ? 1 : -1;
public ResultPane() {
setOpaque(false);
setLayout(null);
result = new JLabel();
Font font = result.getFont();
font = font.deriveFont(Font.BOLD, 26f);
result.setFont(font);
add(result);
HandlerClass handler = new HandlerClass();
result.addMouseListener(handler);
result.addMouseMotionListener(handler);
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point point = result.getLocation();
point.x += xDelta;
point.y += yDelta;
if (point.x < 0) {
point.x = 0;
xDelta *= -1;
} else if (point.x + result.getWidth() > getWidth()) {
point.x = getWidth() - result.getWidth();
xDelta *= -1;
}
if (point.y < 0) {
point.y = 0;
yDelta *= -1;
} else if (point.y + result.getHeight() > getHeight()) {
point.y = getHeight() - result.getHeight();
yDelta *= -1;
}
result.setLocation(point);
repaint();
}
});
timer.start();
}
public void setResult(Number number) {
result.setText(NumberFormat.getNumberInstance().format(number));
result.setSize(result.getPreferredSize());
setVisible(true);
}
}
public class NewPane extends JPanel {
private final ResultPane resultPane;
public NewPane(ResultPane resultPane) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
this.resultPane = resultPane;
JPanel buttons = new JPanel();
buttons.add(new JButton(new LabelAction()));
add(buttons, gbc);
}
public class LabelAction extends AbstractAction {
LabelAction() {
putValue(NAME, "Label");
}
#Override
public void actionPerformed(ActionEvent e) {
resultPane.setResult(123);
}
}
}
private class HandlerClass implements MouseListener, MouseMotionListener {
public void mouseClicked(MouseEvent event) {
resultPane.setResult(0);
}
public void mouseReleased(MouseEvent event) {
}
public void mousePressed(MouseEvent event) {
}
public void mouseExited(MouseEvent event) {
}
public void mouseEntered(MouseEvent event) {
}
public void mouseMoved(MouseEvent event) {
}
public void mouseDragged(MouseEvent event){
}
}
}
resultPane is only defined in the scope of run(). If you want HandlerClass to have access to it you need to give it a definition.
Make resultPane instance variable. You can't access local variable outside the method.
class MovingLabel {
private ResultPane resultPane; // Declare it as instance variable
public MovingLabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
...
resultPane = new ResultPane(); // Initialize the instance variable
...
}
}
}
It's a nice program.
Personally, I put every class in a new file. For me it easier to understand scope. The problem is indeed that the reference in HandlerClass is outside of it's declaration in the other classes.

Categories

Resources