Strange JFrame Behavior

I have the following program that has very strange and undesirable behavior when it starts. It is assumed that it has two buttons: "Start" and "Stop", but when I click "Start", another button appears to the right of "Start". Here is the print screen I'm talking about:

enter image description here

What am I doing wrong and how to fix this ugly problem?

Here is the code:

import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Random; public class TwoButtonsTest { JFrame frame; Timer timer; boolean isClicked; public static void main(String[] args) { TwoButtonsTest test = new TwoButtonsTest(); test.go(); } public void go() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 500); JButton startButton = new JButton("Start"); startButton.addActionListener(new StartListener()); JButton stopButton = new JButton("Stop"); stopButton.addActionListener(new StopListener()); final DrawPanel myDraw = new DrawPanel(); frame.getContentPane().add(BorderLayout.CENTER, myDraw); frame.getContentPane().add(BorderLayout.NORTH, startButton); frame.getContentPane().add(BorderLayout.SOUTH, stopButton); frame.setVisible(true); timer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { myDraw.repaint(); } }); } class StartListener implements ActionListener { public void actionPerformed(ActionEvent e) { //needs to be implemented if(!isClicked) { } isClicked = true; timer.start(); } } class StopListener implements ActionListener { public void actionPerformed(ActionEvent e) { //needs to be implemented timer.stop(); isClicked = false; } } class DrawPanel extends JPanel { public void paintComponent(Graphics g) { int red = (int)(Math.random()*256); int blue = (int)(Math.random()*256); int green = (int)(Math.random()*256); g.setColor(new Color(red, blue, green)); Random rand = new Random(); // following 4 lines make sure the rect stays within the frame int ht = rand.nextInt(getHeight()); int wd = rand.nextInt(getWidth()); int x = rand.nextInt(getWidth()-wd); int y = rand.nextInt(getHeight()-ht); g.fillRect(x,y,wd,ht); } } // close inner class } 

I am also trying to start the start button to do two things. One of them is, of course, to start the animation, but when the “Stop” button is pressed and I press “Start” again, I want it to clear the screen so that it speaks and start the animation again. Any tips on this?

+2
source share
3 answers

You do not call super.paintComponent(Graphics g) in the overriden paintComponent(..) method, which you need to honor the drawing chain and thus the picture of the other components.

This call should also be the first method call:

 @Override public void paintComponent(Graphics g) { super.paintComponent(g); //do painting here } 

A problem may arise that the drawings are not permanent. You should have a way to store drawings and redraw every time. The most common is an ArrayList , which will contain the objects that you need to draw (so you cannot add to the deletion of the list, etc.), you would have to iterate over the list and redraw each object in paintComponent . See my answer here for an example.

  • Also remember to create and process Swing components on the Dispatch Thread Event :

     SwingUtilities.invokeLater(new Runnable() { @Override public void run() { //create UI and components here } }); 
  • Not calling setSize(..) on a JFrame rather override getPreferredSize() in the JPanel and return the appropriate height, which is suitable for all components, than calling JFrame#pack() before the JFrame will be displayed (but after adding all the components).

  • No need for getContentPane().add(..) with Java 6+ add(..) by default for contentPane

  • Do not declare Random i.e. Random r=new Random() every time paintComponent is called, as this will cause less random distributions of values ​​to initiate it once when creating the class and call methods on the instance

Here is the fixed code (with the fixes installed above):

enter image description here

 import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Random; import javax.swing.*; public class TwoButtonsTest { JFrame frame; Timer timer; boolean isClicked; public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TwoButtonsTest test = new TwoButtonsTest(); test.go(); } }); } final DrawPanel myDraw = new DrawPanel(); public void go() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton startButton = new JButton("Start"); startButton.addActionListener(new StartListener()); JButton stopButton = new JButton("Stop"); stopButton.addActionListener(new StopListener()); frame.add(myDraw, BorderLayout.CENTER); frame.add(startButton, BorderLayout.NORTH); frame.add(stopButton, BorderLayout.SOUTH); frame.pack(); frame.setVisible(true); timer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { myDraw.repaint(); } }); } class StartListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { //needs to be implemented if (!isClicked) { } myDraw.clearRects(); isClicked = true; timer.start(); } } class StopListener implements ActionListener { public void actionPerformed(ActionEvent e) { //needs to be implemented timer.stop(); isClicked = false; } } class DrawPanel extends JPanel { private ArrayList<MyRectangle> rects = new ArrayList<>(); private Random rand = new Random(); @Override public void paintComponent(Graphics g) { super.paintComponent(g); addRect(); for (MyRectangle r : rects) { g.setColor(r.getColor()); g.fillRect(rx, ry, r.width, r.height); } } @Override public Dimension getPreferredSize() { return new Dimension(500, 500); } public void clearRects() { rects.clear(); } public void addRect() { // following 4 lines make sure the rect stays within the frame int ht = rand.nextInt(getHeight()); int wd = rand.nextInt(getWidth()); int x = rand.nextInt(getWidth() - wd); int y = rand.nextInt(getHeight() - ht); int red = (int) (Math.random() * 256); int blue = (int) (Math.random() * 256); int green = (int) (Math.random() * 256); rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green))); } } // close inner class } class MyRectangle extends Rectangle { Color color; public MyRectangle(int x, int y, int w, int h, Color c) { super(x, y, w, h); this.color = c; } public Color getColor() { return color; } } 
+4
source

I would like to propose a solution, but so far I have not found it. I can say that the root of the “problem” here is how you draw the center section of your BorderLayout. You override the entire paintComponent () function for this program and have what it creates by placing it in the Center of your BoarderLayout. In this case, every time you press the button, the program calls a redraw to draw the image of the pressed button, but since you also added ANY of the drawn objects to the Center panel, it is also drawn there. Since this particular redraw does not indicate a location, it goes in the upper left corner.

+3
source

I fixed the problem with your button on my Windows XP computer by invoking SwingUtilities.

I formatted your java code.

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; 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; import javax.swing.SwingUtilities; import javax.swing.Timer; public class TwoButtonsTest implements Runnable { JFrame frame; Timer timer; boolean isClicked; public static void main(String[] args) { SwingUtilities.invokeLater(new TwoButtonsTest()); } @Override public void run() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 500); JButton startButton = new JButton("Start"); startButton.addActionListener(new StartListener()); JButton stopButton = new JButton("Stop"); stopButton.addActionListener(new StopListener()); final DrawPanel myDraw = new DrawPanel(); frame.getContentPane().add(BorderLayout.CENTER, myDraw); frame.getContentPane().add(BorderLayout.NORTH, startButton); frame.getContentPane().add(BorderLayout.SOUTH, stopButton); frame.setVisible(true); timer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { myDraw.repaint(); } }); } class StartListener implements ActionListener { public void actionPerformed(ActionEvent e) { // needs to be implemented if (!isClicked) { } isClicked = true; timer.start(); } } class StopListener implements ActionListener { public void actionPerformed(ActionEvent e) { // needs to be implemented timer.stop(); isClicked = false; } } class DrawPanel extends JPanel { @Override public void paintComponent(Graphics g) { int red = (int) (Math.random() * 256); int blue = (int) (Math.random() * 256); int green = (int) (Math.random() * 256); g.setColor(new Color(red, blue, green)); Random rand = new Random(); // following 4 lines make sure the rect stays within the frame int ht = rand.nextInt(getHeight()); int wd = rand.nextInt(getWidth()); int x = rand.nextInt(getWidth() - wd); int y = rand.nextInt(getHeight() - ht); g.fillRect(x, y, wd, ht); } } // close inner class } 

To clear the screen when you click the Start button, you will have to add some methods to the DrawPanel class.

Here is one way to do it.

 class DrawPanel extends JPanel { protected boolean eraseCanvas; public void setEraseCanvas(boolean eraseCanvas) { this.eraseCanvas = eraseCanvas; } @Override public void paintComponent(Graphics g) { if (eraseCanvas) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); } else { int red = (int) (Math.random() * 256); int blue = (int) (Math.random() * 256); int green = (int) (Math.random() * 256); g.setColor(new Color(red, blue, green)); Random rand = new Random(); // following 4 lines make sure the rect stays within the frame int ht = rand.nextInt(getHeight()); int wd = rand.nextInt(getWidth()); int x = rand.nextInt(getWidth() - wd); int y = rand.nextInt(getHeight() - ht); g.fillRect(x, y, wd, ht); } } } // close inner class 
+2
source

All Articles