如何在jpanels之间切换?

4

我还是一个刚接触Java编程的新手,请帮忙指正我可能忽略的任何错误或提供改进程序的提示。

好了,许多问题已经得到解决,现在我有一个CardLayout,但是我仍然对如何使我的管道显示在其中有疑问。

当我尝试添加刷新率计时器和速度计时器时,我遇到了声明和初始化布尔变量的问题。

此外,在编译和运行此游戏时,我会得到文件,例如Game$1.class。是否有一种方法可以清理这些文件,并且有人能解释为什么会发生这种情况吗? 这些对最终产品有影响吗?(当游戏被编译并打包成JAR时。)

当点击播放按钮时,我想将playerIsReady设置为true。从那里开始,当if语句为true时,就切换到一个显示管道的面板,并开始在屏幕上移动管道。 最好是三个实例的管道,每个管道在不同的时间开始,但无论您能提供什么样的帮助都可以。

这段代码中有一些需要修改的地方,所以我注释了一些部分并留下了注释。

这个游戏的其他问题可以在这里找到。

以下是当前的代码:

Game.java

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import javax.swing.SwingUtilities;

public class Game {

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {             
                // the GUI as seen by the user (without frame)
                final CardLayout cl = new CardLayout();
                final JPanel gui = new JPanel(cl);
                // remove if no border is needed
                gui.setBorder(new EmptyBorder(10,10,10,10));

                JPanel menu = new JPanel(new GridBagLayout());
                JButton playGame = new JButton("Play!");
                ActionListener playGameListener = new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        cl.show(gui, "game");
                    }
                };
                playGame.addActionListener(playGameListener);
                Insets margin = new Insets(20, 50, 20, 50);
                playGame.setMargin(margin);
                menu.add(playGame);
                gui.add(menu);
                cl.addLayoutComponent(menu, "menu");

                final JPanel pipes = new Pipes();
                gui.add(pipes);
                cl.addLayoutComponent(pipes, "game");

                JFrame f = new JFrame("Pipes Game");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See https://dev59.com/7Ww05IYBdhLWcg3wmC6_#7143398 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);

                /*if (playerIsReady) { 
                    Timer speed = new Timer(10, new ActionListener() {  //pipe speed
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            pipes.move();
                        }
                    });
                    speed.start();

                    Timer refresh = new Timer(30, new ActionListener() {    //refresh rate
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            pipes.repaint();
                        }
                    });
                    refresh.start();
                }*/
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency
        SwingUtilities.invokeLater(r);
    }
}

Pipes.java

// What import(s) do I need for ArrayList?
public class Pipes {
    List<Pipe> pipes = new ArrayList<Pipe>();

    public Pipes() {
        pipes.add(new Pipe(50, 100));
        pipes.add(new Pipe(150, 100));
        pipes.add(new Pipe(250, 100));
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        for ( Pipe pipe : pipes ){
            pipe.drawPipe(g);
        }
    }
}

PipeObject.java

import java.awt.Graphics;

public class PipeObject {
    //Declare and initialiaze variables
    int x1 = 754;               //xVal start
    int x2 = 75;                //pipe width
                                //total width is 83
    int y1 = -1;                //yVal start
    int y2 = setHeightVal();    //pipe height
    int gap = 130;              //gap height

    public void drawPipe(Graphics g) {

        g.clearRect(0,0,750,500);                       //Clear screen
        g.drawRect(x1,y1,x2,y2);                        //Draw part 1
        g.drawRect(x1-3,y2-1,x2+6,25);                  //Draw part 2
        g.drawRect(x1-3,y2+25+gap,x2+6,25);             //Draw part 3
        g.drawRect(x1,y2+25+gap+25,x2,500-y2-49-gap);   //Draw part 4
    }

    public void move() {
        x1--;
    }

    public int getMyX() {   //To determine where the pipe is horizontally
        return x1-3;
    }

    public int getMyY() {   //To determine where the pipe is vertically
        return y2+25;
    }

    public int setHeightVal() {     //Get a random number and select a preset height
        int num = (int)(9*Math.random() + 1);
        int val = 0;
        if (num == 9)
        {
            val = 295;
        }
        else if (num == 8)
        {
            val = 246;
        }
        else if (num == 7)
        {
            val = 216;
        }
        else if (num == 6)
        {
            val = 185;
        }
        else if (num == 5)
        {
            val = 156;
        }
        else if (num == 4)
        {
            val = 125;
        }
        else if (num == 3)
        {
            val = 96;
        }
        else if (num == 2)
        {
            val = 66;
        }
        else
        {
            val = 25;
        }
        return val;
    }
}

请查看CardLayout - Tdorno
2
每个.java文件中的每个内部类在__javac__执行将它们转换为字节码时都被视为一个新的.class。这就是为什么第一个内部类的Main$1.class。您可以使用java.beans.EventHandler,它不会随意创建类,但要小心使用它们。对于后者,请参见示例CardLayout - nIcE cOw
为了更快获得帮助,请发一个 MCTaRE(最小完整可测试和可读的示例)。 - Andrew Thompson
顺便说一句 - 恭喜你获得50+的声望值。现在你可以在任何问题或答案下留言了。 :) - Andrew Thompson
Pipes 应该是一个 JPanel 类。你想使用 PipeObject 对象而不是 Pipe 对象。你甚至没有一个 Pipe 类。 - Paul Samsotha
3个回答

6
最好的方法是使用CardLayout
注意事项:
- 用ActionListener来替代在矩形上使用MouseListener. - 当鼠标指向该按钮或者通过键盘进行焦点切换时,该按钮将显示焦点; - 该按钮支持键盘访问,并内置多个图标的支持(例如“开始外观”,聚焦、按下等); - 在菜单面板和游戏周围添加一个EmptyBorder提供GUI中的空白区域; - 通过设置边距使按钮变大; - 根据需要调整边距、边框和首选大小。这些大小是由我设定的,以避免截图过大; - 在代码注释中查看更多提示。
代码:
这是生成上述屏幕截图的MCTaRE(最小完整测试和可读性示例)。
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class PipesGame {

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                // the GUI as seen by the user (without frame)
                final CardLayout cl = new CardLayout();
                final JPanel gui = new JPanel(cl);
                // remove if no border is needed
                gui.setBorder(new EmptyBorder(10,10,10,10));

                JPanel menu = new JPanel(new GridBagLayout());
                JButton playGame = new JButton("Play!");
                ActionListener playGameListener = new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        cl.show(gui, "game");
                    }
                };
                playGame.addActionListener(playGameListener);
                Insets margin = new Insets(20, 50, 20, 50);
                playGame.setMargin(margin);
                menu.add(playGame);
                gui.add(menu);
                cl.addLayoutComponent(menu, "menu");

                JPanel pipes = new Pipes();
                gui.add(pipes);
                cl.addLayoutComponent(pipes, "game");

                JFrame f = new JFrame("Pipes Game");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See https://dev59.com/7Ww05IYBdhLWcg3wmC6_#7143398 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency
        SwingUtilities.invokeLater(r);
    }
}

class Pipes extends JPanel {

    Pipes() {
        setBackground(Color.BLACK);
        setForeground(Color.WHITE);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawString("Pipes game appears here..", 170, 80);
    }

    @Override 
    public Dimension getPreferredSize() {
        // adjust to need
        return new Dimension(500,150);
    }
}

谢谢,我会努力添加这个功能。你是如何制作这些漂亮的截图的? - Cyber Storm
"你怎么拍那些漂亮的截图?" 请参考如何创建屏幕截图以说明帖子 - Andrew Thompson
哦,我以为你改变主意了,所以删除了之前的评论。但是我还是回答了你的问题。;) - Andrew Thompson
哈,所以我正在尝试添加这段代码,但我没有复制/粘贴的奢侈。需要无线网络而不是 LTE。无论如何,我有一个错误。它说我需要在public void run()之后加上分号。 - Cyber Storm
哦,10万不错...恭喜! - MadProgrammer
显示剩余4条评论

5
“有没有办法让我将我的GameMenu jpanel添加到我的jframe中,然后将其替换为Pipes jpanel?”像其他人建议的那样,您需要使用CardLayout。这非常简单。个人而言,我总是将我的CardLayout包装在JPanel中,而不是JFrame,只是习惯问题。您要做的是拥有一个mainPanel,它将具有CardLayout。”
CardLayout card = new CardLayout();
JPanel mainPanel = new JPanel();

接下来你需要将你的面板添加到mainPanelCardLayout的作用是将面板层叠,一次只显示一个。第一个添加的面板将在前景中。当你添加面板时,你还需要为它发出,以便可以从中调用。键可以是任何你喜欢的字符串。

mainPanel.add(gameMenu, "menu");
mainPnael.add(pipes, "pipe");

现在只有 gameMenu 面板被显示。要显示 pipes,你只需要使用这个方法: 因此,你可以使用 card.show(mainPanel, "pipes");
无论你想触发什么事件来显示 pipes,只需在该事件处理程序中添加该行代码。你可以在 GameMenu 中添加一个按钮或其他东西,以使移动到 Pipes 面板成为可能。

1
@AndrewThompson.. 我也能识别胡说八道。我们都知道100k是误导性的;-D 还有37个! - Paul Samsotha
@peeskillet 太对了!我不是完全确定你的意思,但考虑到我所做的点赞(成千上万)和我提供的悬赏(总计2150名声点),我想一个月前我本来可以获得10万声望。 - Andrew Thompson
1
@AndrewThompson 99,963 :) - Paul Samsotha
1
@peeskillet 好的,回到了 100K。我不得不承认我曾经为自己的回答争取过那个小勾勾。如果你有什么安慰的话,可以知道这一点:我经常打开一个问题,看到了你的答复,快速地浏览一下,想“解决了!”,点了个赞就把问题关闭了。:) - Andrew Thompson
1
@AndrewThompson 呜呜呜,感动到哭了。 - Paul Samsotha
显示剩余6条评论

3

这个功能可以通过在菜单上单击鼠标来实现。您可以稍后更改为在某个按钮或您想要的任何其他东西上单击。

我向Game类添加了一个MouseListener。当用户按下鼠标时,它会将Pipes JPanel添加到JFrame中,并调用pack方法。

Game.java:

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Game {

    GameMenu menu = new GameMenu();
    Pipes game;
    boolean start = false;
    JFrame f;
    Rectangle2D menuRect = new Rectangle2D.Double(20, 20, 60, 40);

    public Game() {
        f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(menu);
        f.setTitle("Pipe Game");
        f.setResizable(false);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);



        menu.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {

                Point click = new Point(e.getX(), e.getY());
                System.out.println("Clicked on the Panel");

                if(menuRect.contains(click))
                {
                    System.out.println("Clicked inside the Rectangle.");

                    start = true; 
                    menu.setVisible(false);
                    game = new Pipes();
                    f.add(game);
                    f.pack();

                    Timer timer = new Timer(10, new ActionListener() {  //pipe speed
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            game.move();
                        }
                    });
                    timer.start();

                    Timer refresh = new Timer(30, new ActionListener() {    //refresh rate
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            game.repaint();
                        }
                    });
                    refresh.start();   
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
            }
        });

    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Game();
            }
        });
    }
}

有没有办法只在鼠标点击GameMenu框架中显示的矩形内时触发事件?例如,当鼠标按下时,获取鼠标坐标并检查它们是否在矩形内部? - Cyber Storm
你知道矩形的坐标,所以可以手动检查。Rectangle2D中还有一个contains方法。在这里看一下:https://dev59.com/GFrUa4cB1Zd3GeqPou4U - Hugo Sousa
更新了我的答案。现在它会验证您是否点击了矩形内部。 - Hugo Sousa
谢谢,Hugo。这会有所帮助。 - Cyber Storm
-1 是针对 @CyberStorm 的。嗯,我承认这可能不是解决问题的最佳方法,但至少它能工作。不过还是等待更多答案吧。 - Hugo Sousa
是的,我刚刚注意到了。我会对解决方案保持开放的心态,但我仍然可以使用监听器。那部分仍然解决了我的一个问题。 - Cyber Storm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接