Hello,

I am writing a Java program where the first JFrame has choices for number of shapes, width, height and the type of shape (oval, rectangle or both) and a button is clicked that opens up another JFrame where the shapes are drawn with a time delay until the number of shapes equals the user input. Everything works fine except the JButton from the first JFrame is displayed at the top left corner of the second JFrame when the JButton on the first frame is pressed. Any idea how to fix this issue?

import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.JOptionPane;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.BoxLayout;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JDialog;
import java.awt.Dimension;
import java.awt.Component;

public class Interactive extends JPanel implements ActionListener{ 
    private int shape, x, y, width, height;
    private  String shapeType;
    private Random r = new Random();
    private Timer t;
    private JTextField field1, field2, field3, field4;
    private JButton  button;
    private JLabel  label1, label2, label3, label4;
    private JDialog dialog;
    public Interactive(){
        setLayout (new BoxLayout(this, BoxLayout.Y_AXIS));

        label1 = new JLabel ("Number of Shapes", JLabel.CENTER);
        label1.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        field1 = new JTextField (20);
        field1.setMaximumSize(new Dimension(200,20));
        field1.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        label2 = new JLabel ("Width", JLabel.CENTER);
        label2.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        field2 = new JTextField (20);
        field2.setMaximumSize(new Dimension(200,20));
        field2.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        label3 = new JLabel ("Height", JLabel.CENTER);
        label3.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        field3 = new JTextField (20); 
        field3.setMaximumSize(new Dimension(200,20));
        field3.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        label4 = new JLabel ("Shape Type", JLabel.CENTER);
        label4.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        field4 = new JTextField (20);
        field4.setMaximumSize(new Dimension(200,20));
        field4.setAlignmentX(JLabel.CENTER_ALIGNMENT);

        button = new JButton ("Submit");
        button.addActionListener(this);
        button.setAlignmentX(JLabel.CENTER_ALIGNMENT);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(label1);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(field1);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(label2);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(field2);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(label3);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(field3);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(label4);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(field4);
        add(Box.createRigidArea(new Dimension(20, 10)));
        add(button);
    }

    public void actionPerformed (ActionEvent e){
        System.out.println ("Event Handler");
        shape = Integer.parseInt(field1.getText());
        width = Integer.parseInt(field2.getText());
        height = Integer.parseInt(field3.getText());
        String shapeType = field4.getText();
        Draw d = new Draw(shape + 1, width, height, shapeType);
    }

    public static void main (String args[]){
        JFrame f = new JFrame ("Jocelyn's Art");
        Interactive d = new  Interactive();
        f.add(d);
        f.setSize(500, 500);
        f.setVisible (true);
        f.setResizable(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
} 

import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.JOptionPane;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.Timer;
import javax.swing.JFrame;

public class Draw extends JPanel implements ActionListener{
    private int width, height,shape;
    private int count = 0;
    private String shapeType;
    private Random r = new Random();
    private JFrame f;
    private Timer t;
    public Draw (int shape, int width, int height, String shapeType){
        this.width = width;
        this.height = height;
        this.shape = shape;
        this.shapeType = shapeType;
        f = new JFrame ("Jocelyn's Art");
        f.add(this);
        f.setSize(500, 500);
        f.setVisible (true);
        f.setResizable(false);
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        t = new Timer (100, this);
        t.start();
    }

    public void actionPerformed (ActionEvent e){
        repaint();
    }

    public void paintComponent (Graphics g){
        int x = r.nextInt(300);
        int y = r.nextInt(300);
        count++;
        if (shapeType.equalsIgnoreCase ("Oval")){
            g.fillOval (x,y,width, height);
        }
        else if (shapeType.equalsIgnoreCase("Rectangle")){
            g.fillRect (x,y,width,height); 
        }
        else{
            if (count % 2 == 1){
                g.fillOval (x,y,width, height);
            }
            else{
                g.fillRect (x,y,width,height); 
            }
        }

        if (this.shape == count){
            t.stop();
        }
    }
} 

Thank you

Which Java are you using? I tried a recent Java and it complained.

> Compiling...

/str/JavaFiddle.java:19: error: class Interactive is public, should be declared in a file named Interactive.java
public class Interactive extends JPanel implements ActionListener{ 
       ^
/str/JavaFiddle.java:90: error: class Draw is public, should be declared in a file named Draw.java
public class Draw extends JPanel implements ActionListener{
       ^
2 errors
commented: He just posted the files in one place. Each set of imports starts a new file. +15

You have (quite rightly) overridden paintComponent.
super's paintComponent is responsible for clearing/setting the background, so you have missed that functionality and and are starting with whatever is left over in one of Swing's graphics buffers.

your paintComponent method needs to start with a super.paintComponent(g); on its first call to ensure that the background is cleared correctly. (Or you can clear it yourself by filling with a suitable coloured rectangle.)

note that paintComponent may be called by Swing at any time (eg when a window is moved/resized etc) and any number of times, so updating the counter and the random numbers should ideally happen in your timer's actionPerformed, not in PaintComponent.

commented: Thanks for clarifying their posted code is not a single file. Also, your answer makes sense as to the code behavior, that is, it worked as coded. +15

James,

Thanks for your answers. I realize super.paintComponent clears all the previous shapes on the panel. Is there any way I can paint on the panel without clearing all the previous shapes I have drawn? I want all the previous shapes to be visible when I draw a new shape.

I tried including all the random numbers and timer.stop call in the actionPerformed, but that did not change the button from showing up in the top left of the second JFrame.

James,

I just drew the shapes on a buffered image and then painted the image on the JFrame using paint(). Works well. Thank you.

Using double buffering works, but it's unnecessary complexity and overhead. Swing already has double buffering internally.

Threre are also sound reasons for not directly painting in a JFrame... eg what happens when you want to add any Swing controls to that JFrame. Using a subclass of JPanel as in your first code is the right way to go.

All you you need is to clear/initialise the panel on only the first call to paintComponent (use a boolean to control that). Easy.

Edit:

Just to make the point, all your original code needed was one line at the start of your paintComponent:

if (count == 0) super.paintComponent(g);

so very close!

That works, thank you.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.