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
}
Related
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
}
I am currently using the Applet class to create a simple game. Because there is a flickering effect, I've added double-buffering for Graphics components by creating an off-screen buffer like so:
public class AppletTest extends Applet implements Runnable {
Thread thread;
Image img;
Graphics gfx;
public final int WIDTH = 700, HEIGHT = 500;
public void init() {
this.resize(WIDTH, HEIGHT);
thread = new Thread(this);
thread.start();
img = createImage(WIDTH, HEIGHT); // off-screen buffering
gfx = img.getGraphics();
}
public void draw(Graphics g) {
gfx.setColor(Color.BLACK);
gfx.fillRect(0, 0, WIDTH, HEIGHT);
gfx.setColor(Color.WHITE);
gfx.fillRect(50, 50, 100, 100);
gfx.setFont(new Font("Century", Font.BOLD, 30));
gfx.drawString("I feel good sometimes I don't", 200, 200);
g.drawImage(img, 0, 0, this); // draws the off-screen image
}
public void update(Graphics g) {
draw(g);
}
public void run() {
while(true) {
repaint();
try {
Thread.sleep(5);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
If you run the application, all the Graphics (.fillRect, .drawString, etc.) components/methods are drawn on the off-screen buffer. However, my goal is to add a JButton to the applet - and as expected, there's no off-screen loading for the JButton component (which means flickering).
Graphics gfx;
JButton button1;
public void draw(Graphics g) {
setLayout(null);
button1.setBounds(225, 400, 250, 50);
button1.setFont(new Font("Courier", Font.PLAIN, 17));
button1.setForeground(Color.WHITE);
button1.setBackground(Color.DARK_GRAY);
add(button1); // is it possible to draw the JButton on the off-screen buffer?
}
How would you add off-screen loading to a JButton component?
Applet (and JApplet) are officially deprecated, they are no longer supported by Java, Oracle, Browsers (or the community generally)
Swing components are, by default, double buffered. If you work with the painting system correctly, you shouldn't experience any flickering, if you do, it's clear sign that you're doing something wrong.
I would recommend having a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the Swing painting system works.
Swing is single threaded AND not thread safe. This means that you should not perform any long running operations within the context of the Event Dispatching Thread and you should not update the UI from outside the context of the EDT.
Have a look at Concurrency in Swing for more details.
A simple solution to these problems is to make use a Swing Timer, which can be used to schedule regular updates which are executed within the context of the EDT.
See How to Use Swing Timers for more details...
As a basic runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int WIDTH = 700, HEIGHT = 500;
public TestPane() {
setLayout(new GridBagLayout());
add(new JButton("Big fat button"));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.WHITE);
g2d.fillRect(50, 50, 100, 100);
g2d.setFont(new Font("Century", Font.BOLD, 30));
g2d.drawString("I feel good sometimes I don't", 200, 200);
g2d.dispose();
}
}
}
Okay, "But I absolutely, must, no questions asked, use Applet ... 😓, then I feel sorry for you, but that doesn't change the fact that Swing is already double buffered. The above example could easily be applied to a J/Applet simply by creating an instance of the JPanel and adding to an Applet container
Swing makes use of "passive rendering" algorithm, if you absolutely must be complete control, then you can have a look at BufferStrategy which hands complete control of the painting system over to you, but you won't be able to use Swing components, as they are updated by the Swing sub-system
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);
}
i have been programming in python for a while and have been interested in learning Java for a while now so i figured i would give it a go so have mercy i know this is a infinite loop at the moment so tread carefully.
import javax.swing.*;
import java.awt.*;
public class snake extends JFrame{
public static void main(String[] args) {
JFrame screen = new JFrame("Snake");
screen.setSize(640, 480);
screen.setResizable(false);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = screen.getContentPane();
screen.setVisible(true);
while(true) {
pane.add(new Render());
}
}
}
class Render extends JComponent {
Render(){
}
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0,0,640,480);
g.setColor(Color.green);
g.fillRect(100,100,25,25);
}
}
my current goal for this small program is to loop my code so that every time my while loop iterates i would like the flow of code to update the screen and redraw anything so in the future the rectangle could move. From my tests i have been running the first iteration of the loop runs through the method inside of my Render class and then after that it stays in the (i believe that is a constructor in java)
Render() {
}
piece of code.where it cycles through every iteration how i would like it for the method. I have tried to call the method from inside the constructor and that did not work. How can i route the flow of the program so every iteration of my loop the program goes through that method? Thanks for your time.
Start by taking a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting in Swing works...
This...
while(true) {
pane.add(new Render());
}
is going to cause issues. If it doesn't block the Event Dispatching Thread, it will run so fast as it cause other threads to stall (or run more slowly) and simply continuously add new instances of Render to pane, which I'm pretty sure you don't want.
It is generally encouraged to override paintComponent instead of paint, as it can cause less issues. You should also, be convention, call super.paintComponent before doing any custom painting.
Instead, simply create an instance of Render and add it to the frame
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Snake {
public static void main(String[] args) {
new Snake();
}
public Snake() {
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 Render());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class Render extends JComponent {
Render() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(0, 0, 640, 480);
g.setColor(Color.green);
g.fillRect(100, 100, 25, 25);
}
}
}
You are likely going to want to perform some animation, take a look at Concurrency in Swing and How to use Swing Timers for more details
For example
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();
}
}
}