java 如何通过单击 JButton 添加 JPanel?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14011397/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 14:43:59  来源:igfitidea点击:

How to add JPanel by clicking JButton?

javaswingjpaneljbuttonactionlistener

提问by user1924939

I'm trying to create a small GUI, it has 2 JButtons, and 2 JPanels with some drawing animation on each of them. By default it must show first JPanel, and by clicking on second JButton I want to see my second JPanel. So : I create JFrame, Panel1 and Panel2, where I have drawn my animations, create Button1 and Button2 and adding to them ActionListeners. I have also MainPanel which has in a fields variable i. By changing this "i" my constructor adds to MainPanel either Panel1 (default) or Panel2 (by clicking on JButton2 I change i). Than I add this MainPanel to my frame. So my question : in the class MainPanel I have refreshMe method, what should I write there to make my GUI working properly? Thanks. Here is my code:

我正在尝试创建一个小的 GUI,它有 2 个 JButton 和 2 个 JPanel,每个都带有一些绘图动画。默认情况下,它必须显示第一个 JPanel,通过单击第二个 JButton,我想看到我的第二个 JPanel。所以:我创建了 JFrame、Panel1 和 Panel2,在那里我绘制了我的动画,创建了 Button1 和 Button2 并向它们添加了 ActionListeners。我还有 MainPanel,它在一个字段中具有变量 i。通过更改此“i”,我的构造函数将 Panel1(默认)或 Panel2 添加到 MainPanel(通过单击 JButton2,我更改了 i)。比我将此 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();
            }
        });

    }

}

回答by David Kroukamp

In order to reflect changes after adding/removing or resizing a component that is on a visible container call revalidate()and repaint()on the containers instance after adding/removing or resizing the component.

为了反映添加/移除或调整大小部件,其上形成可视容器呼叫之后改变revalidate()repaint()在容器上例如添加/移除或调整所述部件之后。

Though this will not work in your code the main reason being inside JButton classes you recreate a new instance of MainPanelwhen in fact the 2 JButtonsshould share the single instance which is being used (you could pass MainPanel instance to the JButtons constructors, but you shouldn't really be extending a JButtonunless adding custom functionality):

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

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() {
    }
}

A few other suggestions on your code:

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

  • Dont extend JButtonclass unnecessarily, simply create an instance of JButtonlike you did with JFrameand call methods on JButtoninstance.

  • Dont forget to create/manipulate Swing components on Event Dispatch Thread, via SwingUtilities.invokeLater(..)block, read herefor more.

  • 不要JButton不必要地扩展类,只需JButton像您一样创建一个实例并JFrameJButton实例上调用方法。

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

Here is your code fixed (above suggestions ect implemented):

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

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);
    }
}

However Id suggest something simpler, fortunately you have 2 choices:

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

1) Use CardLayoutwhich will allow you to flip between multiple components on a single JFrame/container.

1)使用CardLayout它可以让您在单个JFrame/容器上的多个组件之间切换。

Here is an example I made:

这是我做的一个例子:

enter image description here

在此处输入图片说明

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) Use removeAll()technique i.e call frame.getContentPane().removeAll()which will remove all components currently on JFrameand than add the new content and call revalidate()and repaint()(also might want to add pack()in there) on JFrameinstance to reflect changes. Though Id recommend CardLayout.

2)使用removeAll()技术,即通话frame.getContentPane().removeAll()将当前删除所有组件上JFrame,比添加新的内容和通话revalidate()repaint()(可能还需要增加pack()在那里),JFrame例如,以反映变化。虽然我推荐CardLayout.

回答by Weibo

I think you can just use CardLayoutto implement your function. Please refer to here

我认为你可以CardLayout用来实现你的功能。请参考这里