Swing repaint() doesn't work in loop or thread - 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

Java AWT How to draw objects with delay

I would like to draw a new random shape every 2 seconds.
I already have a window, that shows immediately some shapes. I tried to mess around with Timer to make new things appear in the window after a few seconds, but it didn't work, or the whole program freezes. Is it a good idea to use Timer? How should I implement it, to make it work?
import javax.swing.*;
import java.awt.*;
import java.util.Random;
class Window extends JFrame {
Random rand = new Random();
int x = rand.nextInt(1024);
int y = rand.nextInt(768);
int shape = rand.nextInt(2);
Window(){
setSize(1024,768);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(0, 52, 255));
switch(shape) {
case 0:
g.fillOval(x, y, 50, 50);
break;
case 1:
g.fillRect(x,y,100,100);
break;
}
repaint();
}
}
public class Main {
public static void main(String[] args) {
Window window = new Window();
}
}
I would also like to draw some random shapes. Is it ok, to use switch in paint method for this purpose? I would make a random variable, if it's 1 it would paint rectangle, if it's 2 it would paint oval etc.
First of all, do not change the way JFrame gets painted (with other words, do not override paintComponent() of a JFrame). Create an extending class of JPanel and paint the JPanel instead. Secondly, do not override paint() method. Override paintComponent(). Thirdly, always run Swing applications with SwingUtilities.invokeLater() since they should run in their own thread named EDT (Event dispatch thread). Finally, javax.swing.Timer is what you are looking for.
Take a look at this example. It paints an oval shape in random X,Y every 1500ms.
Preview:
Source code:
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class DrawShapes extends JFrame {
private ShapePanel shape;
public DrawShapes() {
super("Random shapes");
getContentPane().setLayout(new BorderLayout());
getContentPane().add(shape = new ShapePanel(), BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setLocationRelativeTo(null);
initTimer();
}
private void initTimer() {
Timer t = new Timer(1500, e -> {
shape.randomizeXY();
shape.repaint();
});
t.start();
}
public static class ShapePanel extends JPanel {
private int x, y;
public ShapePanel() {
randomizeXY();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
public void randomizeXY() {
x = (int) (Math.random() * 500);
y = (int) (Math.random() * 500);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new DrawShapes().setVisible(true));
}
}
First, don't subclass JFrame; subclass JPanel instead, and place that panel in a JFrame.
Second, don't override paint() - override paintComponent() instead.
Third, create a Swing Timer, and in its actionPerformed() method make the changes you want and then call yourPanel.repaint()

Iterating through a list and drawing a colored square

I have a colored box, I want it to change color every 1/2 second, however I want my code to run as well.
I'm using Java AWT's Graphic Api to draw using g.fillRect(568, 383, 48, 48); where g is wrapped to 'Graphics.'
So you'd think that its simple right?
Color[] colors
colors = new Color[4];
colors[0] = new Color(Color.red);
colors[1] = new Color(Color.blue);
colors[2] = new Color(Color.green);
colors[3] = new Color(Color.yellow);
for(int i = 0; i < colors.length; i++){
g.setColor(colors[i]);
g.fillRect(568, 383, 48, 48);
}
This is all cool but the problem is that none of my program runs when this for loop is running...
I think I can make the game 'Multi-Threaded' which means it can do more than one thing at a time but I have no idea how to do this and it sounds hard, all help appreciated!
Most UI frameworks aren't thread safe, so you need to beware of that. For example, in Swing, you could use a Swing Timer to act as a pseudo loop. Because the Timer notifies the ActionListener from within the context of the Event Dispatching Thread, it makes it safe to update the UI or the state of the UI from within, without risking thread race conditions
Take a look at Concurrency in Swing and How to use Swing Timers for more details
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 JavaApplication430 {
public static void main(String[] args) {
new JavaApplication430();
}
public JavaApplication430() {
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 Color[] colors;
private int whichColor = 0;
public TestPane() {
colors = new Color[4];
colors[0] = Color.red;
colors[1] = Color.blue;
colors[2] = Color.green;
colors[3] = Color.yellow;
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
whichColor++;
repaint();
if (whichColor >= colors.length) {
whichColor = colors.length - 1;
((Timer)(e.getSource())).stop();
}
}
});
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();
g2d.setColor(colors[whichColor]);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
}
}
I cannot imagine how you could create an interactive game if your code is single thread. To change the box color periodically you will have to put your thread to sleep. If your game is not multi-thread, then this will freeze your application, preventing user interaction. You will find a lot of interesting materials about programming with threads in Java:
http://docs.oracle.com/javase/tutorial/essential/concurrency/
http://www.ibm.com/developerworks/library/j-thread/
http://moderntone.blogspot.com.br/2013/02/a-simple-java-multithreading-example.html
Just google it!

Java: Using graphics component within an ActionListener

I have two separate class and driver files, and in the class file I create the paint method:
public void paint(Graphics g){
g.drawLine(......
....
//along with all of my other draw commands
}
Further down in the code, I create a JButton and within this button's action listener I don't know how to use a Graphics object to create more graphics in the JFrame. Should I be adding something to my driver to make this happen, or is there a way to use these graphics within my action listener? Thank you, and any help is appreciated.
You need to draw everything within the paint method. The actionPerformed should only change the state of something already in the paint method, and then call repaint. For example
boolean drawHello = true;
boolean drawWorld = false;
protected void paintComponent(Graphics g) {
super.paintCompoent(g);
if (drawHello)
g.drawString("Hello", 50, 50);
if (drawWorld)
g.drawString("World", 10, 10);
}
Then in your actionPerformed, you can change the state of drawWorld to true and call repaint().
public void actionPerformed(ActionEvent e) {
drawWorld = true;
repaint();
}
So as you can see, everything should be drawn in the paintComponent method. You can just hide and paint renderings, and make them "visible" from a action command. You should already have predefined what could posibly be drawn. Then just change the state of it rendering
And as #MadPrgrammer pointed out, you should not be painting on top-level containers like JFrame. Instead paint on a custom JPanel or JComponent and override the paintComponent method, instead of JFrame and paint
Here's an example where I draw a new square every time the button is pressed. If look at the code, you will see that in the paintComponent method, I loop through a list of Squares and draw them, and in the actionPerformed all I do is add a new Square to the List and call repaint()
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class AddSquares {
private int R = 0;
private int G = 0;
private int B = 0;
private int xLoc = 0;
private int yLoc = 0;
List<Square> squares = new ArrayList<>();
private JButton addSquare = new JButton("Add Square");
private RectsPanel panel = new RectsPanel();
public AddSquares() {
addSquare.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(R, G, B);
squares.add(new Square(xLoc, yLoc, color));
panel.repaint();
R += 10;
G += 20;
B += 30;
xLoc += 20;
yLoc += 20;
}
});
JFrame frame = new JFrame("Draw Squares");
frame.add(panel, BorderLayout.CENTER);
frame.add(addSquare, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private class RectsPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Square square : squares) {
square.drawSquare(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
private class Square {
int x = 0;
int y = 0;
Color color;
public Square(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void drawSquare(Graphics g) {
g.setColor(color);
g.fillRect(x, y, 75 ,75);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AddSquares addSquares = new AddSquares();
}
});
}
}
It's difficult to be 100%, but it would seem as you don't understand how custom painting is performed in Swing.
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing.
Essentially, painting is arranged by the Repaint Manager, which decides what and when something should be painted. It then calls (through a chain of methods) the paint method of the components it thinks need to be updated, passing it a reference to a Graphics context that should be used to actually paint on.
Basically, when ever your paint method is called, you should create paint the current state of your painting.
You should avoid overriding paint and instead use paintComponent from classes the extend JComponent
Your question is a little on the vague side as to what you are actually wondering about but generally speaking:
We don't override paint in Swing, we override paintComponent.
If you are already aware of this, you may be overriding paint because you are doing it on a JFrame and you found that JFrame does not have a paintComponent method. You shouldn't override paint on a JFrame. Instead, create a JPanel or something to put inside the frame and override paintComponent on the panel.
Question about the ActionListener.
It sounds like you are wanting to do painting outside of paintComponent in which case probably the best way is to do painting to a separate Image. Then you paint the Image on to the panel in paintComponent. You can also put an Image in a JLabel as an ImageIcon. Here is a very simple drawing program using MouseListener that demonstrates this (taken from here):
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
class PaintAnyTime {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new PaintAnyTime();
}
});
}
final BufferedImage image = (
new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB)
);
final JFrame frame = new JFrame();
final JLabel label = new JLabel(new ImageIcon(image));
final MouseAdapter drawer = new MouseAdapter() {
Graphics2D g2D;
#Override
public void mousePressed(MouseEvent me) {
g2D = image.createGraphics();
g2D.setColor(Color.BLACK);
}
#Override
public void mouseDragged(MouseEvent me) {
g2D.fillRect(me.getX(), me.getY(), 3, 3);
label.repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
g2D.dispose();
g2D = null;
}
};
PaintAnyTime() {
label.setPreferredSize(
new Dimension(image.getWidth(), image.getHeight())
);
label.addMouseListener(drawer);
label.addMouseMotionListener(drawer);
frame.add(label);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
#MadProgrammer has already linked to the articles that I was going to link to.

Paint method not working on full screen - Java

I am trying to create a full screen with a paint method. I want "This is the day" in pink with a blue background to show up. The problem is, is that when I run my program, It shows up with what I painted on my screen with my toolbar and applications instead of showing up a blue background with "This is the day" in the middle in pink. Some code is below.
public static void main(String args[])
{
DisplayMode dm1 =
new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
RunScreen runProgram = new RunScreen();
runProgram.run(dm1);
}
public void run(DisplayMode dm)
{
getContentPane().setBackground(Color.BLUE);
setForeground(Color.PINK);
setFont(new Font("Arial", Font.PLAIN, 24));
FullScreen s = new FullScreen();
try
{
s.setFullScreen(dm, this);
try
{
Thread.sleep(5000);
}
catch (Exception e)
{
}
}
finally
{
s.restoreScreen();
}
}
#Override
public void paint(Graphics g)
{
g.drawString("This is the day", 200, 200);
}
Thread.sleep(5000);
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling Thread.sleep(n) implement a Swing Timer for repeating tasks or a SwingWorker for long running tasks. See Concurrency in Swing for more details.
This seems to work just fine...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFullScreen {
public static void main(String[] args) {
new TestFullScreen();
}
public TestFullScreen() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
device.setFullScreenWindow(frame);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String text = "Hello";
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g.drawString(text, x, y);
}
}
}
Beware, the "window" that you request to be made into a full screen window may not be the actually window used by the system.
You should avoid overriding paint and use paintComponent instead. You should also avoid overriding paint of top level containers (like JFrame).
Check out Performing Custom Painting and Painting in AWT and Swing for more details...
To hide toolbar and etc. Use setUndecorated
runProgram.setUndecorated(true);
runProgram.run(dm1);
To show up a blue background with "This is the day" in the middle in pink, set the color in paint (or paintComponent)
g.setColor(Color.BLUE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.PINK);
g.drawString("This is the day", 200, 200);
Second, you shouldn't involve with GUI out of the EDT (Event Dispatch Thread). You should do those thing like this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setFont(new Font("Arial", Font.PLAIN, 24));
}
});
Third, you shouldn't overrides paint of top level containers. (I don't mean the #Override notation). You should do as Andrew Thompson mentioned:
Repaint method is not entered when the frame is full screen. So this way is useful for repainting for swing JFrame.
public void actionPerformed(ActionEvent arg0) {
if (getExtendedState() == 6) {//6 full-screen, 0 normal, -1 minimized
repaint();
} else {
repaint();
}
}

repaint() method in java not working properly

am doing that program to paint the mouse location in a panel , the program works fine but after like 10 seconds it stops painting the points... any help?
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Draw extends JPanel {
public static int newx;
public static int newy;
public void paint(Graphics g) {
Mouse mouse = new Mouse();
mouse.start();
int newx = mouse.x;
int newy = mouse.y;
g.setColor(Color.blue);
g.drawLine(newx, newy, newx, newy);
repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.white);
frame.setSize(2000,2000 );
frame.setVisible(true);
frame.getContentPane().add(new Draw());
frame.revalidate();
frame.getContentPane().repaint();
}
}
public void paint(Graphics g) should be public void paintComponent(Graphics g).
And you isn't supposed to call repaint() inside this method.
You should add an mouse listener outside this method, too.
An adapted example from Java Tutorials
public class MouseMotionEventDemo extends JPanel
implements MouseMotionListener {
//...in initialization code:
//Register for mouse events on blankArea and panel.
blankArea.addMouseMotionListener(this);
addMouseMotionListener(this);
...
}
public void mouseMoved(MouseEvent e) {
Point point = e.getPoint();
updatePanel(point); //create this method to call repaint() on JPanel.
}
public void mouseDragged(MouseEvent e) {
}
}
}
You call repaint within the paint method, causing an infinite loop. Swing Timers are preferred for running periodic updates on components.
For custom painting in Swing the method paintComponent rather than paint should be overridden not forgetting to call super.paintComponent.

Categories