1

I'm trying to add a JPanel to my JFrame within an actionListener method, but it appears only after the second click on the button. This is a portion of my code where panCours is a JPanel and ConstituerData the targeted JFrame :

addCours.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            panCours.setBounds(215, 2, 480, 400);
            panCours.setBorder(BorderFactory.createTitledBorder("Saisir les données concernant le cours"));
            ConstituerData.this.getContentPane().add(panCours);
        }
    });

I don't understand why it doesn't appear as soon as I click on the button. Any explanation and help about how to fix this ?

sk001
  • 561
  • 6
  • 27
  • See if calling `pack` on the `JFrame` in the end of `actionPerformed` solves your problem. – Hugo Sousa Mar 05 '14 at 21:49
  • This call solves the problem, but creates another one : it sets each of the dimensions of my `JFrame` to 0. – sk001 Mar 05 '14 at 21:54
  • @HugoSousa - He's using a null layout by the looks of things, so that's not going to help much. Try calling repaint on your JFrame, or JPanel. – Rudi Kershaw Mar 05 '14 at 21:55
  • Exactly, I'm using a `null` layout. @RudiKershaw, it solves the problem. – sk001 Mar 05 '14 at 21:58
  • @sk001 take a look here http://stackoverflow.com/questions/13633503/java-setbounds-not-working-with-jpanel – Hugo Sousa Mar 05 '14 at 22:00
  • The question that comes to mind is, why are using a `null` layout? – MadProgrammer Mar 05 '14 at 22:56
  • After taking a look at certain layouts, I am not able to add my components to the right place. Although, using a `null` layout might not be recommended. – sk001 Mar 06 '14 at 00:32

2 Answers2

3

You'll need to add a call to repaint(); (as well as probably revalidate();) to get the JPanel to show immediately. A basic example demonstrating your problem (and the solution) below;

public class Test {

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);

        JButton button = new JButton("Test");                       
        button.setBounds(20, 30, 100, 40);
        button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                JPanel panel = new JPanel();
                panel.setBackground(Color.red);
                panel.setBounds(215, 2, 480, 480);
                frame.add(panel);
                frame.revalidate(); // Repaint here!! Removing these calls
                frame.repaint(); // demonstrates the problem you are having.

            }
        }); 

        frame.add(button);
        frame.setSize(695, 482);
        frame.setVisible(true);              

    }
}

The above said, (as suggested by others) it's only right that I recommend against the use of a null layout in future. The swing layouts are a little awkward to begin with, but they will help you a great deal in the long run.

Rudi Kershaw
  • 12,332
  • 7
  • 52
  • 77
  • 3
    Please, don't suggest the people use `null` layouts...it's just propergating bad advice and introducing more problems – MadProgrammer Mar 05 '14 at 22:46
  • Thanks, @RudiKershaw. It's now correct if I have only one `JPanel` to add. But I want to switch between many `JPanel` according to the choice of a button. Using this solution, the `JPanel` shown is not the the one corresponding to the `JButton` clicked. How can I do ? – sk001 Mar 06 '14 at 00:27
  • @MadProgrammer - Quite right, I will update my answer to suggest a layout. But generally, once someone is deap into a project they wont want to change. Suggesting the use of a layout manager will help them in the future greatly, but it doesn't help them right now. – Rudi Kershaw Mar 06 '14 at 08:14
  • @sk001 - Are you remembering to remove the previous `JPanel` when you switch them and calling `repaint()` afterwards? I am afraid a new answer for multiple panels might be out of the scope of the question. You might have to post up a new one with more of your code demonstrating the issue. – Rudi Kershaw Mar 06 '14 at 08:26
  • @RudiKershaw, I'll then make a new post which describes explicitly my problem. – sk001 Mar 06 '14 at 09:47
2

the answer can be found in the following snippet: you need to revalidate() the contentPane, not repaint the frame. you can add any panel you want to the contentpane like this. if you declare contentPane as a private field you dont need your getContentPane() call. contentPane is global so it can be reffered to directly from anywhere within the class. be careful about NullPointerExeptions which can be thrown if you refer to it before initialising.

public class testframe extends JFrame {

private JPanel contentPane;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                testframe frame = new testframe();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the frame.
 */
public testframe() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = new JPanel();
    setContentPane(contentPane);

    JButton btnNewButton = new JButton("New button");
    btnNewButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            JPanel a = new JPanel();
            contentPane.add(a);
            contentPane.revalidate();
        }
    });
    contentPane.add(btnNewButton);  
}

}
PeterYoshi
  • 102
  • 1
  • 3
  • 10
  • http://stackoverflow.com/questions/1097366/java-swing-revalidate-vs-repaint - To be absolutely safe, perhaps revalidating and repainting is in order. But certinaly not *just* revalidating. – Rudi Kershaw Mar 06 '14 at 08:27