Avoiding while loops in java swing [duplicate] - java

I have the following code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.text.View;
public class ex10 extends JPanel {
private int x=1;
int y=1;
//Constructor
public ex10() {
while(true) {
System.out.println("x ->"+ x);
System.out.println("y ->" + y);
x = randomposition(x);
y = randomposition(y);
this.repaint();
}
}
public int randomposition(int value) {
Random random = new Random();
if (random.nextBoolean() == true) {
if (value+1 != 500) {
value++;
}
}
else {
if (value-1 != 0) {
value--;
}
}
return value;
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
g.setColor(Color.green);
g.fillRect(x, y, 20, 20);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(new ex10());
}
}
Unfortunately, when this.repaint() is called, the point isn't being displayed, but I still got the System.out.println. I tried setting a new thread separatedly, but to no avail.
I tried out some other solution (invokelater, and paintimmediately), also to no avail.
My goal is to set a green point which wanders on the screen.
Do you have any solution?

Your while (true) is blocking the Swing event thread putting the application to sleep.
For simple animation and game loop, use a Swing Timer. If you have long running code that needs to be in the background, then use a background thread such as a SwingWorker, but taking care to make sure that all calls that change the state of your Swing components should be done on the Swing event thread.
For example, you could change this:
while(true) {
System.out.println("x ->"+ x);
System.out.println("y ->" + y);
x = randomposition(x);
y = randomposition(y);
this.repaint();
}
to this that uses a Swing Timer (javax.swing.Timer):
int timerDelay = 20;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x = randomposition(x);
y = randomposition(y);
repaint();
}
}).start();
Regarding DSquare's comments:
Indeed you are not running your GUI on the Swing event thread, something you should be doing, and yet your while true loop is still freezing your painting because your infinite loop prevents the component from completely creating itself.
As noted above, you should in fact start all Swing GUI's on the Swing event thread which you could do by placing the Swing creation code into a Runnable and queuing the Runnable on the event thread via the SwingUtilities method, invokeLater.
You need to call the super's paintComponent method in your paintComponent override so that the JPanel can do its housekeeping graphics works including clearing "dirty" pixels.
For example, change this:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(new ex10());
}
to this:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(new Ex10());
}
});
}
And change this:
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
g.setColor(Color.green);
g.fillRect(x, y, 20, 20);
}
to this:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.green);
g.fillRect(x, y, 20, 20);
}

Related

Timer in for loop java swing [duplicate]

Ok, so the program's purpose is to just draw and oval and move it across the screen. The code compiles on Eclipse without an error, but when run, no oval is drawn or moved across the screen. I have been researching, and it seems that threads have to do a lot with this, but do I need one for this simple program? I am obviously new to GUI programming with Swing so I would appreciate an explanation or link to one for any additions to the program regarding threads or such related concepts.
public class Game extends JPanel
{
int x =0;
int y =0;
private void moveBall()
{
x+=1;
y+=1;
}
public void paint (Graphics g)
{
super.paint(g);
g.fillOval(x, y, 30, 30);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Animation");
Game game = new Game();
frame.add(game);
frame.setVisible(true);
frame.setSize(300,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
while (true)
{
game.moveBall();
game.repaint();
}
}
}
The likely problem is, that thread is running too fast for the UI, the UI is been shown well after the "ball" has left the visible area.
You need to do a couple of things...
First, you need to make sure that the updates are scheduled properly within the Event Dispatching Thread and secondly, that there is a short delay between updates. For example, 25fps is about a 40 millisecond delay between updates, 60fps is about 16 milliseconds
There are a number of ways to achieve this, depending what it is you hope to achieve, for example, you could simply use Thread.sleep to cause the thread to pause for a small amount of time between updates. The problem with this is Swing is not thread safe and all updates to the UI should be made within the context of the Event Dispatching Thread.
While you program is only simply, it's possible that a paint cycle could run while you updating it's state, resulting in a dirty update.
Another solution might be to use a Swing Timer which will allow you schedule updates at a regular interval which are triggered within the context of the Event Dispatching Thread, making it safer to use.
Have a look at Concurrency in Swing and How to use Swing Timers for more details.
As an example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BallAnimation {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new BallAnimation();
}
public BallAnimation() {
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 int x = 0;
private int y = 0;
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveBall();
repaint();
}
});
timer.start();
}
protected void moveBall() {
x++;
y++;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(x, y, 30, 30);
g2d.dispose();
}
}
}
As a side note, unless you really have reason to do so, you should avoid overriding paint and instead use paintComponent
try your loop with sleep as simplest way to fit your code. main is actually a thread. and JFrame creates its own thread.
while (true)
{
game.moveBall();
game.repaint();
try { Thread.sleep(50); } catch (Exception e){}
}
and I just realized, you dont paint your whole screen with a default color.
change your paint method to this
public void paint (Graphics g)
{
super.paint(g);
g.setColor(Color.white); //default color
g.fillRect(0, 0, getWidth(), getHeight()); // fill whole canvas
g.setColor(Color.black); //change color
g.fillOval(x, y, 30, 30); // draw oval
}

Trying to draw lines in java over an image i already drew but i can't get it on top of the image?

I have to draw an archery target with two black lines in the innermost circle that forms a cross, but every time i adjust the lines so that the lines are closer to the centre it goes behind the image instead of appearing on top. How can I stop this? Does it need to have a separate set of instructions entirely?
This is my code:
package sumshapes;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class SumShapes extends JFrame
implements ActionListener {
private JPanel panel;
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.drawLine(250, 200, 250, 200);
g.drawOval(140,90,200,200);
g.setColor(Color.BLACK);
g.fillOval(140,90,200,200);
g.drawOval(162,109,155,155);
g.setColor(Color.BLUE);
g.fillOval(162,109,155,155);
g.drawOval(183,129,112,112);
g.setColor(Color.RED);
g.fillOval(183, 129, 112, 112);
g.drawOval(210,153,60,60);
g.setColor(Color.YELLOW);
g.fillOval(210, 153, 60, 60);
g.setColor(Color.BLACK);
}
public static void main(String[] args) {
SumShapes frame = new SumShapes();
frame.setSize(500,400);
frame.setBackground(Color.yellow);
frame.createGUI();
frame.setVisible(true);
}
private void createGUI(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container window = getContentPane();
window.setLayout (new FlowLayout());
}
public void actionPerformed(ActionEvent event) {
Graphics paper = panel.getGraphics();
paper.drawLine(20,80,120,80);
}
}
All your drawing should go into the paintComponent method of a lightweight component, such as a JPanel.
There should never be a need to call getGraphics. If you wish to change the drawing upon a particular action you should a) program the logic into paintComponent b) alter the logic in the Action c) call repaint on the Component
For example:
private JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);//call parent method first thing
//paint here
}
#Override
public Dimension getPreferredSize(){//provided so you can size this component as necessary
return new Dimension(500,400);
}
};
....
frame.add(panel);
frame.pack();
As an aside, I'd recommend placing all calls to to Swing components on the EDT - this means wrapping your Swing calls in the main method with SwingUtilities. eg
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
SumShapes frame = new SumShapes();
....
}
});
}

Simple Java Animation using JFrame and JPanel

Ok, so the program's purpose is to just draw and oval and move it across the screen. The code compiles on Eclipse without an error, but when run, no oval is drawn or moved across the screen. I have been researching, and it seems that threads have to do a lot with this, but do I need one for this simple program? I am obviously new to GUI programming with Swing so I would appreciate an explanation or link to one for any additions to the program regarding threads or such related concepts.
public class Game extends JPanel
{
int x =0;
int y =0;
private void moveBall()
{
x+=1;
y+=1;
}
public void paint (Graphics g)
{
super.paint(g);
g.fillOval(x, y, 30, 30);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Animation");
Game game = new Game();
frame.add(game);
frame.setVisible(true);
frame.setSize(300,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
while (true)
{
game.moveBall();
game.repaint();
}
}
}
The likely problem is, that thread is running too fast for the UI, the UI is been shown well after the "ball" has left the visible area.
You need to do a couple of things...
First, you need to make sure that the updates are scheduled properly within the Event Dispatching Thread and secondly, that there is a short delay between updates. For example, 25fps is about a 40 millisecond delay between updates, 60fps is about 16 milliseconds
There are a number of ways to achieve this, depending what it is you hope to achieve, for example, you could simply use Thread.sleep to cause the thread to pause for a small amount of time between updates. The problem with this is Swing is not thread safe and all updates to the UI should be made within the context of the Event Dispatching Thread.
While you program is only simply, it's possible that a paint cycle could run while you updating it's state, resulting in a dirty update.
Another solution might be to use a Swing Timer which will allow you schedule updates at a regular interval which are triggered within the context of the Event Dispatching Thread, making it safer to use.
Have a look at Concurrency in Swing and How to use Swing Timers for more details.
As an example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BallAnimation {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new BallAnimation();
}
public BallAnimation() {
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 int x = 0;
private int y = 0;
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveBall();
repaint();
}
});
timer.start();
}
protected void moveBall() {
x++;
y++;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(x, y, 30, 30);
g2d.dispose();
}
}
}
As a side note, unless you really have reason to do so, you should avoid overriding paint and instead use paintComponent
try your loop with sleep as simplest way to fit your code. main is actually a thread. and JFrame creates its own thread.
while (true)
{
game.moveBall();
game.repaint();
try { Thread.sleep(50); } catch (Exception e){}
}
and I just realized, you dont paint your whole screen with a default color.
change your paint method to this
public void paint (Graphics g)
{
super.paint(g);
g.setColor(Color.white); //default color
g.fillRect(0, 0, getWidth(), getHeight()); // fill whole canvas
g.setColor(Color.black); //change color
g.fillOval(x, y, 30, 30); // draw oval
}

AWT graphics, object disappears briefly

I am trying to learn how to make a graphics program, but some of the methods in java AWT are giving me unexpected results.
I have made a window, and I place a rectangle and that works. I want another figure, a circle, to appear after 1 second. I have tried the wait(x) method, which just places the circle immediately, and now I have tried the Thread.sleep(x) method, which does work, however I get the following behaviour:
After one second, the circle is displayed on the screen, but after a split second it disappears again, and another split second later it reappears and stays on the screen. I don't want it to temporarily disappear. What am I doing wrong?
import java.awt.*;
class Example extends Canvas{
public static void main(String[] args){
Example graphicProgram = new Example();
Frame graphics = new Frame();
graphics.setSize(300, 300);
graphics.add(graphicProgram);
graphics.setVisible(true);
}
public Example(){
setSize(200, 200);
setBackground(Color.white);
}
public void paint(Graphics g){
g.fillRect(20, 150, 100, 100);
try{
Thread.sleep(1000);
} catch (Exception ex){
}
g.fillOval(150, 20, 100, 100);
}
}
Never call Thread.sleep from within a paint type of method. Doing this will make your GUI completely unresponsive.
Yes, do call the super painting method from within your painting method (as per muhammad's answer).
You should not call Thread.sleep(...) from the event thread either as this too will freeze your application.
You should skip doing AWT and move to Swing.
When you do so, do your drawing in the paintComponent(Graphics g) method of a JComponent or JPanel object.
Call the super's paintComponent(g) within your paintComponent method override.
Use a Swing Timer to do any delay or animation.
e.g.,
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class DrawFoo extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final Stroke BASIC_STROKE = new BasicStroke(3f);
private static final Color RECT_COLOR = Color.blue;
private static final Color OVAL_COLOR = Color.red;
private boolean drawCircle = false;
private int rectX = 20;
private int rectY = 150;
private int rectWidth = 100;
public DrawFoo() {
int delay = 1000;
Timer timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
drawCircle = true;
repaint();
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(BASIC_STROKE);
g2.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, rectWidth, rectWidth);
if (drawCircle) {
g2.setColor(OVAL_COLOR);
g.fillOval(rectY, rectX, rectWidth, rectWidth);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawFoo());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
put this line as the first statment in your paint method super.paint(g);
it will be good to place also Graphics2D g2 = (Graphics2D)g; after above statment to use the improved performance and extra methods offered by Graphics2D

Thread help. Paint [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
Can someone please help me with the comment lines except the ones where it asks to delete things. Thank you!
public class ex1011c extends JApplet implements ActionListener
{
// get rid of all winkbutton code
JButton winkbutton = new JButton("Wink At You");
boolean wink = false, first = true;
Container c;
public void init()
{
c = getContentPane();
c.setLayout(new FlowLayout());
c.setBackground(Color.blue);
winkbutton.setForeground(Color.cyan);
c.add(winkbutton);
winkbutton.addActionListener(this);
}
// get rid of actionPerformed
public void actionPerformed(ActionEvent e)
{
wink = !wink;
repaint();
}
public void paint(Graphics g)
{
/* if first time, draw the face and non winking eye,
set first to false */
super.paint(g);
g.setColor(Color.yellow);
g.fillOval(50, 50, 100, 100);
g.setColor(Color.black);
g.fillOval(85, 80, 10, 20);
/* cover just the eye that winks (if winking or not, but do not
cover anything else), switch the wink boolean */
// draw the full eye or winking eye
if (wink)
g.fillOval(105, 88, 10, 5);
else
g.fillOval(105, 80, 10, 20);
// go to sleep for a second
// call repaint
}
// override update to lesson flicker
}
Simple, don't sleep/pause/block or otherwise impede the Event Dispatching Thread.
The EDT, is amongst other things, responsible for process paint requests, anything to stops it from running (like Thread.sleep) will stop it from updating the screen.
Remember, just because you paint something to the Graphics context, doesn't mean it gets rendered to the output.
Instead, use a javax.swing.Timer
Take a look at Concurrency in Swing, Performing Custom Painting and Painting in AWT and Swing for more details.
I would also, strongly, encourage you against overriding any paint method of a top level container like JApplet. Instead, use something like JPanel and override it's paintComponent method instead.
Apart from potability, you gain the benefit of double buffering which top level containers don't have...
For example...
Blinking in JFrame Java
With simple example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Blinky {
public static void main(String[] args) {
new Blinky();
}
public Blinky() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BlinkPane extends JPanel {
private boolean wink;
public BlinkPane() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
wink = !wink;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - 10) / 2;
int eyeHeight = 20;
if (wink) {
eyeHeight = 5;
}
int y = (getWidth() - eyeHeight) / 2;
g.fillOval(x, y, 10, eyeHeight);
g2d.dispose();
}
}
}

Categories

Resources