如何通过单击 JButton 添加 JPanel?

Posted

技术标签:

【中文标题】如何通过单击 JButton 添加 JPanel?【英文标题】:How to add JPanel by clicking JButton? 【发布时间】:2012-12-10 06:55:33 【问题描述】:

我正在尝试创建一个小型 GUI,它有 2 个 JButton 和 2 个 JPanel,每个都有一些绘图动画。默认情况下,它必须显示第一个 JPanel,通过单击第二个 JButton,我想查看我的第二个 JPanel。所以:我创建了 JFrame、Panel1 和 Panel2,在那里我绘制了我的动画,创建了 Button1 和 Button2 并向它们添加了 ActionListener。我还有 MainPanel,它在字段变量 i 中。通过更改此“i”,我的构造函数将 Panel1(默认)或 Panel2(通过单击 JButton2 我更改 i)添加到 MainPanel。比我将此 MainPanel 添加到我的框架中。所以我的问题是:在 MainPanel 类中我有 refreshMe 方法,我应该在那里写什么来使我的 GUI 正常工作?谢谢。这是我的代码:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GuiTest 

    public static void main(String[] args) 
        JFrame f = new JFrame();
        MainPanel myPanel = new MainPanel();
        f.add(myPanel);
        Button1 button1 = new Button1();
        Button2 button2 = new Button2();
        myPanel.add(button1);
        myPanel.add(button2);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    


class MainPanel extends JPanel 
    Panel1 p1 = new Panel1();
    Panel2 p2 = new Panel2();
    public int i = 1;  //this is being changed later by clicking JButton
    // I use this setter later in actionPerformed in order to change i
    public void setI(int i) 
        this.i = i;
    

    MainPanel()  
        if (i == 1) 
            this.add(p1);
        
        if (i == 2) 
            this.add(p2);
        
    

    public void refreshMe() 
        // Need some help here:
        // I don't know what should I write, how to make a repaint of myPanel?
        System.out.println("just test, if the method refreshMe working by clicking some button");
    


class Panel1 extends JPanel 

    public Panel1() 
        this.setBackground(Color.BLUE);
        // a lot of drawing stuff going on here
    

    @Override
    public Dimension getPreferredSize() 
        return new Dimension(200, 200);
    


class Panel2 extends JPanel 

    public Panel2() 
        this.setBackground(Color.GREEN);
        // a lot of drawing stuff going on here
    

    @Override
    public Dimension getPreferredSize() 
        return new Dimension(200, 200);
    



class Button1 extends JButton 
    MainPanel someObj1 = new MainPanel();

    Button1() 
        setText("Show Annimation A");
        addActionListener(new ActionListener() 

            @Override
            public void actionPerformed(ActionEvent e) 
                someObj1.setI(1);
                System.out.println("The variable i is now: " + someObj1.i);
                someObj1.refreshMe();

            
        );
    



class Button2 extends JButton 
    MainPanel someObj2 = new MainPanel();

    Button2() 
        setText("Show Annimation B");
        addActionListener(new ActionListener() 

            @Override
            public void actionPerformed(ActionEvent e) 
                someObj2.setI(2);
                System.out.println("The variable i is now: " + someObj2.i);
                someObj2.refreshMe();
            
        );

    


【问题讨论】:

【参考方案1】:

为了在添加/删除或调整可见容器上的组件后反映更改,在添加/删除或调整组件大小后,在容器实例上调用 revalidate()repaint()

虽然这在您的代码中不起作用,但主要原因是在 JButton 类中,您重新创建 MainPanel 的新实例,而实际上 2 JButtons 应该共享正在使用的单个实例(您可以传递 MainPanel 实例到JButtons 构造函数,但你不应该真正扩展JButton,除非添加自定义功能):

class Button2 extends JButton 
    MainPanel someObj2 = new MainPanel();//you create an instance of MainPanel which isnt even showing and than do changes on that, this way you will never see any of the changes

    Button2() 
    

关于您的代码的其他一些建议:

不要不必要地扩展 JButton 类,只需像使用 JFrame 一样创建 JButton 的实例并调用 JButton 实例上的方法。

别忘了在Event Dispatch Thread 上创建/操作Swing 组件,通过SwingUtilities.invokeLater(..) 块,阅读here 了解更多信息。

这是您的代码已修复(上述建议等已实施):

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test 

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            @Override
            public void run() 
                JFrame f = new JFrame();

                final MainPanel myPanel = new MainPanel();
                f.add(myPanel);

                JButton button1 = new JButton("Show Animation A");
                JButton button2 = new JButton("Show Animation B");

                button1.addActionListener(new ActionListener() 
                    @Override
                    public void actionPerformed(ActionEvent e) 
                        myPanel.setI(1);
                        System.out.println("The variable i is now: " + myPanel.i);
                        myPanel.refreshMe();
                    
                );
                button2.addActionListener(new ActionListener() 
                    @Override
                    public void actionPerformed(ActionEvent e) 
                        myPanel.setI(2);
                        System.out.println("The variable i is now: " + myPanel.i);
                        myPanel.refreshMe();
                    
                );

                myPanel.add(button1);
                myPanel.add(button2);
                myPanel.checkPanel();

                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                f.pack();
                f.setVisible(true);
            
        );
    


class MainPanel extends JPanel 

    Panel1 p1 = new Panel1();
    Panel2 p2 = new Panel2();
    public int i = 1;  //this is being changed later by clicking JButton
    // I use this setter later in actionPerformed in order to change i

    public void setI(int i) 
        this.i = i;
    

    public void refreshMe() 
        checkPanel();

        revalidate();
        repaint();
        // Need some help here:
        // I don't know what should I write, how to make a repaint of myPanel?
        System.out.println("just test, if the method refreshMe working by clicking some button");
    

    public void checkPanel() 
        if (i == 1) 
            this.add(p1);
            this.remove(p2);//or it will remain there as this is default flowlayout
         else if (i == 2) 
            this.add(p2);
            this.remove(p1);
        
    


class Panel1 extends JPanel 

    public Panel1() 
        this.setBackground(Color.BLUE);
        // a lot of drawing stuff going on here
    

    @Override
    public Dimension getPreferredSize() 
        return new Dimension(200, 200);
    


class Panel2 extends JPanel 

    public Panel2() 
        this.setBackground(Color.GREEN);
        // a lot of drawing stuff going on here
    

    @Override
    public Dimension getPreferredSize() 
        return new Dimension(200, 200);
    

不过我建议一些更简单的东西,幸运的是你有 2 个选择:

1) 使用CardLayout,这将允许您在单个JFrame/container 上的多个组件之间切换。

这是我做的一个例子:

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test 

    private final static String PANEL1 = "panel 1";
    private final static String PANEL2 = "panel 2";

    public Test() 
        initComponents();
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            @Override
            public void run() 
                new Test();
            
        );
    

    private void initComponents() 
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel1 = new JPanel();
        panel1.add(new JLabel("Panel 1"));

        JPanel panel2 = new JPanel();
        panel2.add(new JLabel("Panel 2"));

        //Create the panel that contains the "cards".
        final JPanel cards = new JPanel(new CardLayout());
        cards.add(panel1, PANEL1);
        cards.add(panel2, PANEL2);

        //create button to allow chnage to next card
        JButton buttonNext = new JButton(">");
        buttonNext.addActionListener(new ActionListener() 
            @Override
            public void actionPerformed(ActionEvent ae) 
                CardLayout cl = (CardLayout) (cards.getLayout());//get cards
                cl.next(cards);
            
        );

        //create button to allow chnage to previous card
        JButton buttonPrev = new JButton("<");
        buttonPrev.addActionListener(new ActionListener() 
            @Override
            public void actionPerformed(ActionEvent ae) 
                CardLayout cl = (CardLayout) (cards.getLayout());//get cards
                cl.previous(cards);
            
        );

        //create panel to hold buttons which will allow switching between cards
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(buttonPrev);
        buttonPanel.add(buttonNext);


        frame.add(cards);
        frame.add(buttonPanel, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    

2) 使用removeAll() 技术,即调用frame.getContentPane().removeAll(),这将删除当前在JFrame 上的所有组件,然后添加新内容并调用revalidate()repaint()(也可能要添加pack() in那里)在JFrame 实例上以反映更改。虽然我推荐CardLayout

【讨论】:

感谢您更正我的代码和示例,这很有帮助!是的,CardLayout 解决了我的问题。【参考方案2】:

我认为您可以使用CardLayout 来实现您的功能。请参考here

【讨论】:

以上是关于如何通过单击 JButton 添加 JPanel?的主要内容,如果未能解决你的问题,请参考以下文章

让 JButton 打开一个新的 JPanel

java删除一个组件

为什么我的JButton在放入JPanel的构造函数时不显示?

如何使用默认 FlowLayout 在 JPanel 中将 JButton 居中?

在 JPanel.getComponents() 中循环时如何获取 JButton 属性

在类之间传递值