1

Hi, I'm new to Java and I have the following problem:

I created a JFrame and I want the JPanel to change when clicking a JButton. That does almost work.The only problem is that the program creates a new window and then there are two windows. One with the first JPanel and one with the second JPanel. Here is my current code:

first class:

public class Program {

    public static void main (String [] args) {

        new window(new panel1());

    }
}

second class:

import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Window extends JFrame {

    private static final long serialVersionUID = 1L;

    Window(JPanel panel) {

        setLocation((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 200,
                    (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 100);
        setSize(400, 200);
        setTitle("test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        setContentPane(panel);
        setVisible(true);

    }
}

third class:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

public class Panel1 extends JPanel {

    private final long serialVersionUID = 1L;

    Panel1() {

        JButton nextPanelButton = new JButton("click here");

        add(nextPanelButton);

        ActionListener changePanel = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                new window(new panel2());
            }
        };
        nextPanelButton.addActionListener(changePanel);

    }
}

fourth class:

public class Panel2 extends JPanel {

    private static final long serialVersionUID = 1L;

    Panel2() {

        JLabel text = new JLabel("You pressed the Button!");

        add(text);

    }
}

But I just want to change the JPanel without opening a new window. Is there a way to do that?

Thanks in advance!

  • *"I want the JPanel to change when clicking a JButton"* - this screams `CardLayout` to me. Another issue is you seem to have a lack of understanding of some basic OO paradigms, like "Observer Pattern" or "Model View Controller" which are techniques you could use to solve this problem – MadProgrammer Oct 01 '17 at 10:34

3 Answers3

1

This is a demo

import javax.swing.*;

public class Main {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new MainFrame("Title").setVisible(true);
        });
    }
}

MainFrame.java

import javax.swing.*;
import java.awt.*;

public class MainFrame extends JFrame {
    private JPanel viewPanel;

    public MainFrame(String title) {
        super(title);
        createGUI();
    }

    private void createGUI() {
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setLayout(new BorderLayout());
        setMinimumSize(new Dimension(600, 480));

        viewPanel = new JPanel(new BorderLayout());
        add(viewPanel, BorderLayout.CENTER);

        showView(new View1(this));
        pack();
   }

   public void showView(JPanel panel) {
        viewPanel.removeAll();
        viewPanel.add(panel, BorderLayout.CENTER);
        viewPanel.revalidate();
        viewPanel.repaint();
   }
}

View1.java

import javax.swing.*;
import java.awt.*;

public class View1 extends JPanel {
    final private MainFrame owner;

    public View1(MainFrame owner) {
        super();

        this.owner = owner;
        createGUI();
    }

    private void createGUI() {
        setLayout(new FlowLayout());
        add(new JLabel("View 1"));

        JButton button = new JButton("Show View 2");
        button.addActionListener(event -> {
            SwingUtilities.invokeLater(() -> owner.showView(new View2(owner)));
        });

        add(button);
    }
}

View2.java

import javax.swing.*;
import java.awt.*;

public class View2 extends JPanel {
    final private MainFrame owner;

    public View2(MainFrame owner) {
        super();

        this.owner = owner;
        createGUI();
    }

    private void createGUI() {
        setLayout(new FlowLayout());
        add(new JLabel("View 2"));

        JButton button = new JButton("Show View 1");
        button.addActionListener(event -> {
            SwingUtilities.invokeLater(() -> owner.showView(new View1(owner)));

        });

        add(button);
    }
}
mr mcwolf
  • 2,574
  • 2
  • 14
  • 27
  • 1
    Don't use `updateUI`, it's not doing what you think it's doing and is very inefficient - instead, use `revalidate` and `repaint` – MadProgrammer Oct 01 '17 at 10:33
  • `revalidate` and `repaint` not work in this case. I replace `updateUI()` with `setVisible(false)`/`setVisible(true)` – mr mcwolf Oct 01 '17 at 10:50
  • 1
    Then you're doing something wrong - `updateUI` is used to reload the Look and Feel delegate when it changes to update the UI to the new style - Make sure you use `revalidate` on the container which has been changed – MadProgrammer Oct 01 '17 at 10:51
  • I did a test: I created a transparent background for the panels and after running the program and chanching the JPanel you could see the "old" JPanel behind... Can I avoid that? –  Oct 03 '17 at 15:58
  • if there is no code I can not help. according to the description, perhaps the transparent background is not created correctly, which leads to improper drawing of the panels. you can use github to share the project code that does not work correctly. – mr mcwolf Oct 03 '17 at 17:38
  • I did another test and there wasn't any probroblem... I think I created the background incorrectly, as you said... –  Oct 04 '17 at 15:30
0

First of all, take a look at Java naming conventions, in particular your class names should start with a capitalized letter.

If you want to avoid to open a new window every time you click the button, you could pass your frame object to Panel1 constructor, and setting a new Panel2 instance as the frame content pane when you click the button. There is also no need to pass Panel1 to Window constructor (please note that Window class is already defined in java.awt package, it would be better to avoid a possible name clash renaming your class ApplicationWindow, MyWindow or something else).

You could change your code like this (only relevant parts):

public class Program
{
    public static void main (String [] args) {
        SwingUtilities.invokeLater (new Runnable () {
            @Override public void run () {
                new Window ().setVisible (true);
            }
        };
    }
}
class Window extends JFrame
{
    // ...

    Window () {
        // ...
        setContentPane(new Panel1 (this));
    }
}
class Panel1 extends JPanel
{
    // ...

    Panel1 (JFrame parent) {
        // ...
        ActionListener changePanel = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                parent.setContentPane (new Panel2 ());
            }
        };
        // ...
}

Also note the SwingUtilities's invokeLater call, which is the best way to initialise your GUI in the EDT context (for more info look at this question).

Finally, you could avoid to create a new Panel2 instance every time you click the button, simply by using a CardLayout. Take a look at the official tutorial.

Ansharja
  • 1,237
  • 1
  • 14
  • 37
  • I'd argue that the sub components shouldn't have the responsibility of managing the frame, it's giving them way to much power and limits there re-use. A observer pattern (perhaps implemented in a MVC) would be a far better solution – MadProgrammer Oct 01 '17 at 10:36
  • That doesn't work... Or maybe it does and I just did it wrong :-) I didn't try out the CardLayout, because I got another answer that worked but I've heared from it. –  Oct 04 '17 at 15:35
  • And you are right... I should start the class names with a capitalized letter! –  Oct 04 '17 at 15:37
0

This is an old post, but it may be useful to answer it in a simplified way. Thanks to mr mcwolf for the first answer.

If we want to make 1 child jframe interact with a main jframe in order to modify its content, let's consider the following case. parent.java and child.java. So, in parent.java, we have something like this:

Parent.java

public class Parent extends JFrame implements ActionListener{
    //attributes
    //here is the class we want to modify
    private some_class_to_modify = new some_class_to_modify();
    //here is a container which contains the class to modify
    private JPanel container = new JPanel();
    private some_class = new some_class();
    private int select;
    //....etc..etc
    //constructor
    public Parent(){
        this.setTitle("My title");
        //etc etc
        
        //etc....etc
        container.add(some_class_to_modify,borderLayout.CENTER);
    }
    //I use for instance actionlisteners on buttons to trigger the new JFrame
    public void actionPerformed(ActionEvent arg0){  
        if((arg0.getSource() == source_button_here)){
            //Here we call the child class and send the parent's attributes with "this"
            Child child = new Child(this);
        }
        //... all other cases
    }//Here is the class where we want to be able to modify our JFrame. Here ist a JPanel (Setcolor)
    public void child_action_on_parent(int selection){
        this.select = selection;
        System.out.println("Selection is: "+cir_select);
        if(select == 0) {
            //Do $omething with our class to modify
            some_class_to_modify.setcolor(Color.yellow);
        }
        
    }

In child.java we would have something like this:

public class Child extends JFrame implements ActionListener {
    //Again some attributes here
    private blabla;
    //Import Parent JFrame class
    private Parent owner;
    private int select_obj=0;
    //Constructor here and via Parent Object Import
    public Child(Parent owner){
      /*By calling the super() method in the constructor method, we call the parent's 
      constructor method and gets access to the parent's properties and methods:*/
        super();
        this.owner = owner;
        this.setTitle("Select Method");
        this.setSize(400, 400);
        this.setContentPane(container);
        this.setVisible(true);
      }
    
    class OK_Button  implements ActionListener {
        public void actionPerformed(ActionEvent e) {     
                
                Object Selection = select;
                if(Selection == something) {
                    select_obj=0;
                    valid = JOptionPane.showConfirmDialog(null,"You have chosen option 1. Do you want to continue?","Minimum diameter",2);
                }
                System.out.println("Option is:"+valid);
                if(valid == 0) {
                    setVisible(false);
                    //Here we can use our herited object to call the child_action_on_parent public class of the Parent JFrame. So it can modify directly the Panel
                    owner.child_action_on_parent(select_obj);
                }
            }
    }
Andy McRae
  • 525
  • 7
  • 15