Why is my Panel's repaint not being called? - java

I've got a problem which I just can't seem to get around involving a small animation. For a project we are designing a J9 application to run on a PDA (DELL Axim X51). The problematic code (shown below) is when a mouse click is detected to run a small animation in a nested panel. If the animation is run independently of the mouse click, it works fine and repaints perfectly. When the method is called when a mouse click is recognized the repaint at every animation interval us ignored and only once the animation is completed will the panel be repainted.
I think it might have something to do with when a mouse click is recognized the application is synchronized with itself stopping any internal method calls to paint OR that the paint operation does not have high enough priority over the mouse click.
My code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class StatusLoader extends Panel implements Runnable{
int progress;
public static void main(String[] args) {
Frame f = new Frame();
StatusLoader mp = new StatusLoader();
mp.setBackground(Color.yellow);
f.add(mp);
f.setSize(300,300);
f.show();
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
mp.start();
}
public void start(){
Thread t = new Thread(this);
t.run();
}
public void run(){
for (int i = 0; i < 10; i++) {
progress+=10;
this.repaint(0);
System.out.println(i);
try{
Thread.sleep(100);
}catch(InterruptedException ex){}
}
}
public void paint(Graphics g){
System.out.println("called repaint");
g.setColor(Color.red);
g.setFont(new Font("Sansserif",2,24));
FontMetrics fm = g.getFontMetrics();
int stringWidth = fm.stringWidth("Loading");
g.drawString("Loading", getWidth()/2-stringWidth/2, getHeight()/2);
g.setFont(new Font("Sansserif",2,12));
fm = g.getFontMetrics();
stringWidth = fm.stringWidth("Map ziles for the new zoom level");
g.drawString("Map tiles for the new zoom level", getWidth()/2-stringWidth/2, getHeight()/2+30);
g.drawRect(getWidth()/2-100, getHeight()/2+60, 200, 10);
g.setColor(Color.blue);
g.fillRect(getWidth()/2-100, getHeight()/2+60, progress*2, 10);
}
}

In StatusLoader.start, you use Thread.run instead of Thread.start. That means StatusLoader.run is executed on the events thread, which is a Very Bad Thing. Change it to t.start().

Related

JFrame Exit on close does not appear in java

Take the example from Oracle, there are some examples in your documentation.
My idea is the following:
I have achieved that my application has a transparent background, but the minimize and close application buttons do not appear
This is my code:
main
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
public class Textmovie extends JFrame {
/*
public Textmovie() {
//setLayout(new GridBagLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
*/
public static void main(String[] args) {
JFrame jf = new JFrame("");
jf.setUndecorated(true);
jf.setBackground(new Color(0,0,0,10));
//jf.setOpacity(0.55f);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new texscroll());
jf.setSize(720,480);
jf.setVisible(true);
}
}
Part 2
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
/**
*
* #author inide
*/
public class texscroll extends JPanel {
int x =510 , y = 25;
public texscroll() {
setOpaque(false);
}
#Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2 = (Graphics2D)g;
Font font = new Font("Arial",Font.BOLD + Font.PLAIN,15);
g2.setFont(font);
g2.setColor(Color.BLACK);
String string = "stackoverflow stackoverflow stackoverflow stackoverflow";
g2.drawString(string ,x,y);
try{Thread.sleep(14);}
catch(Exception ex)
{
};
x-=1;
if(x==-10*string.length()){
x= 510;
}
repaint();
// System.out.println(string.length() );
}
}
And this is shown when running in NetBeans IDE 8.0.2
They can explain to me what I have to do to make the buttons appear (minimize and close application).
If you actually dig into the code based on the exception:
Exception in thread "AWT-EventQueue-0" java.awt.IllegalComponentStateException: The frame is decorated
at java.desktop/java.awt.Frame.setBackground(Frame.java:989)
You'll find that it's impossible to make a frame transparent AND be decorated...
#Override
public void setBackground(Color bgColor) {
synchronized (getTreeLock()) {
if ((bgColor != null) && (bgColor.getAlpha() < 255) && !isUndecorated()) {
throw new IllegalComponentStateException("The frame is decorated");
}
super.setBackground(bgColor);
}
}
The fact that the tutorials show it working is irrelevant and an error on the part of the tutorials.
It "might" have been possible in earlier "unreleased" versions of the API (using AWTUtilities), but it simply no longer possible
Now, we've got that out the way, this, inside paint...
try {
Thread.sleep(14);
} catch (Exception ex) {
};
x -= 1;
if (x == -10 * string.length()) {
x = 510;
}
repaint();
is not how you do animation in Swing
This is just going to cause you no end of issues, as nothing is committed to the native peer until AFTER the paintComponent exist (this is how double buffering works)
See Concurrency in Swing for more details.
A more appropriate solution might look something like...
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.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Textmovie extends JFrame {
public static void main(String[] args) {
new Textmovie();
}
public Textmovie() throws HeadlessException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame jf = new JFrame("");
jf.setUndecorated(true);
jf.setBackground(new Color(0, 0, 0, 10));
//jf.setOpacity(0.55f);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new texscroll());
jf.pack();
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
});
}
public static class texscroll extends JPanel {
private int x = 510, y = 25;
private String string = "stackoverflow stackoverflow stackoverflow stackoverflow";
public texscroll() {
Font font = new Font("Arial", Font.BOLD + Font.PLAIN, 15);
setFont(font);
setForeground(Color.BLACK);
setOpaque(false);
Timer timer = new Timer(14, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
x -= 1;
if (x == -10 * string.length()) {
x = 510;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(720, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2 = (Graphics2D) g;
g2.drawString(string, x, y);
}
}
}
See How to Use Swing Timers for more details
jf.setUndecorated(true);
makes the title bar invisible and that includes the minimize and close buttons so you should remove that line (because it's false by default)
It’s because you’re calling jf.setUndecorated(true). This method removes the the title bar, which contains the minimize and maximize buttons.
Unfortunately, the window have to be undecorated to have a system title bar, but the look and feel can provide a title bar. To enable it, you have to call this before your frame is made visible:
JFrame.setDefaultLookAndFeelDecorated(true);

Why doesn't this Java Graphics display in OS X?

I was given some demo code during class that worked on the Windows computer in the lab but doesn't work the same way on my 2010 MacBook, using Sierra.
Making the changes suggested in Java Graphics Not Displaying In OS X didn't fix my problem. I've also tried resizing the window, which changes the animation a bit -- it pops up intermittently after the resize. If I increase the Thread.sleep() time and resize, then the animation improves, but it still is choppy.
Why doesn't the code work on my MacBook and how can I get it to work?
The original code (which works on Windows 10 but not my Mac):
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GraphicsTestA extends JFrame{
int x1 = 60;
int x2 = 150;
public static void main(String [] args){
GraphicsTestA gta = new GraphicsTestA();
gta.animate();
}
private void animate()
{
while(true)
{
for(int i=0; i <100; i ++)
{
x1 = x1 + 1;
x2 = x2 - 1;
try
{
Thread.sleep(100);
}
catch(Exception ex)
{}
repaint();
}
x1 = 60;
x2 = 150;
}
}
public GraphicsTestA() {
setSize(200,200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
this.validate();
}
public void paint(Graphics g) {
super.paint(g);
g.drawString("Hello World",80,80);
g.drawLine(x1,60,x2,150);
g.drawRect(100,40,30,30);
}
}
So a number of issues:
Extending from JFrame
It's generally discouraged to extend directly from a top level container like JFrame, as you rarely add any new re-usable functionality to it and it locks you into a single use case.
It's also a compound component, that is, there are a number of components which reside on it, which one of the main causes of issues for you.
Instead, start with something like JPanel and then add it to any other container you need.
Overriding paint
As a general rule, you should avoid overriding paint, it's to low level for most situations, and in the case of top level containers, just messes things up. Instead, start with paintComponent
See Performing Custom Painting and Painting in AWT and Swing for more details
Thread violations
As has already been pointed out, Swing is not thread safe AND it's single threaded.
This means that you should never update the UI or any values the UI relies on from outside the Event Dispatching Thread. It also means you should never perform any long running or blocking operations inside the context of the EDT either.
In simple cases, a Swing Timer is more then powerful enough to do the job. See How to use Swing Timers for more details
Putting it all together...
So, putting all that together might end up looking something like...
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 static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GraphicsTestA {
public static void main(String[] args) {
new GraphicsTestA();
}
public GraphicsTestA() {
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 {
int x1 = 60;
int x2 = 150;
int loops = 0;
public TestPane() {
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x1 = x1 + 1;
x2 = x2 - 1;
loops++;
if (loops > 100) {
x1 = 60;
x2 = 150;
((Timer) e.getSource()).stop();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawString("Hello World", 80, 80);
g2d.drawLine(x1, 60, x2, 150);
g2d.drawRect(100, 40, 30, 30);
g2d.dispose();
}
}
}
In swing, you're not supposed to invoke swing things outside of the swing thread. Windows lets you get away with some slop, but Java on the mac is super picky about it.
In this code, the constructor makes swing calls, but it's invoked from the main thread. You can try to split that out so the constructor is called by the Swing thread, but then there are problems with the animate method.
The animate method shouldn't be called by the Swing thread because it's calling sleep(), and pausing the swing thread is a bad idea™. However, it's also calling repaint(), which is a swing call and that needs to be called by the Swing thread.
I suggest you use EventQueue.invokeLater to construct your window, and then figure out how to use Swing Workers or some other appropriate mechanism to do your animation.

Unable to create a button on top of a rectangle

Here is a piece of code :
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class QuitButton extends JPanel implements ActionListener
{
static JButton button = new JButton("Panic");
Color[] colors = new Color[9];
boolean pressed = false;
public QuitButton()
{
button.addActionListener(this);
colors[0] = Color.RED;
colors[1] = Color.BLUE;
colors[2] = Color.GREEN;
colors[3] = Color.YELLOW;
colors[4] = Color.BLACK;
colors[5] = Color.PINK;
colors[6] = Color.MAGENTA;
colors[7] = Color.ORANGE;
colors[8] = Color.CYAN;
}
#Override
public void actionPerformed(ActionEvent e)
{
pressed = true;
}
public static void main(String args[])
{
JFrame frame = new JFrame("Do NOT Panic!!");
QuitButton qb = new QuitButton();
frame.add(qb);
frame.add(button);
frame.setLayout(new FlowLayout());
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
//frame.pack();
button.requestFocus();
qb.gameLoop();
}
public void gameLoop()
{
while (true)
{
repaint();
try
{
Thread.sleep(200);
} catch (Exception e)
{
}
}
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if (pressed == false)
{
super.paint(g2d);
g2d.setColor(Color.GRAY);
g2d.fillRect(0, 0, 400, 400);
} else
{
super.paint(g2d);
Random r = new Random();
int min = 0;
int max = 8;
int index = r.nextInt(max - min) + min;
g2d.setColor(colors[index]);
g2d.fillRect(0, 0, 400, 400);
}
}
The purpose of this program: The rectangle should be grey before but when I click the panic button colours should start changing.
Please don't get confused with the name of the class which is QuitButton.
But my rectangle is not occupying the entire window. Instead I am getting a teeny tiny rectangle like this : http://g.recordit.co/xJAMiQu6fM.gif
I think it is because of the layout I am using and I haven't specified anywhere that the button will be on top. Probably that's why they are coming side by side. I am new to GUI creation and thank you for your help.
You seem to be making some guesses on how to do this, which is not a good way to learn to use a library. Your first step should be to check the relevant tutorials on this, most of which will be found here: Swing Info Since this appears to be homework, I'm not going to give you a code solution but rather suggestions on how to improve:
Override paintComponent, not paint since the latter gives double buffering and is less risky (less painting of borders and child component problems)
In your paintComponent override, be sure to call the super's paintComponent method first to clear "dirty" pixels.
Use a Swing Timer, not a while loop for your game loop. This will prevent your while loop from freezing the Swing event thread, a problem that can freeze your program. Google the tutorial as it is quite helpful.
Do your randomization within the ActionListener's code (here likely the ActionListener for your Swing Timer), not within the painting code. The painting code should not change the state of the object but rather should only display the object's state.
FlowLayout will respect a component's preferredSize, and your component's preferred size is 0,0 or close to it. Change this. Best to override public Dimension getPreferredSize() and return a Dimension that matches your Rectangle's size.
Avoid using "magic" numbers, such as for your rectangle's size, and instead use constants or fields.
Call repaint() within your Timer's ActionListener so the JVM knows to paint the component.

Swing repaint() doesn't work in loop or thread

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);
}

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();
}
}

Categories