【问题标题】:How can I switch between jpanels?如何在 jpanel 之间切换?
【发布时间】:2014-02-06 01:51:59
【问题描述】:

我对 java 编程还是很陌生,所以请帮助我纠正我可能忽略的任何错误或提供有关如何改进此程序的提示。

好的,已经解决了很多问题,现在我有了一个 CardLayout,但我仍然对如何让我的管道显示在里面有疑问。

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

另外,当我编译和运行这个游戏时,我得到了诸如Game$1.class 之类的文件。有没有办法让我清理这个,有人可以解释为什么会这样吗?这些对成品有影响吗? (当游戏被编译并打包成 JAR 时。)

我想在单击播放按钮时将 playerIsReady 设置为 true。从那里开始,当 if 语句为真时,切换到显示管道的面板,并开始在屏幕上移动管道。最好是该管道的 3 个实例,每个实例都在不同的时间开始,但是您可以提供任何帮助都可以。

其中一些代码需要工作,所以我已经注释掉了一些部分并留下了注释。

我对这个游戏的其他问题可以找到here

这是我的当前代码

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://stackoverflow.com/a/7143398/418556 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);
    }
}

管道.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
  • .java 文件中的每个内部类在执行 javac 以将它们转换为字节码时都被视为新的.class。这就是为什么Main$1.class 是您看到的第一个内部类。您可以使用java.beans.EventHandler,它不会随机创建类,但请谨慎使用。对于后者,请参阅 example 以获取 CardLayout
  • 为了尽快获得更好的帮助,请发布MCTaRE(经过测试和可读的最小完整示例)。
  • 顺便说一句 - 快乐 50+ 代表。现在您可以在 any 问题或答案上留下 cmets。 :)
  • Pipes 应该是 JPanel 类。并且您想使用 PipeObject 对象而不是 Pipe 对象。你甚至没有Pipe

标签: java swing jframe jpanel awt


【解决方案1】:

解决此问题的最佳方法是使用CardLayout

注意事项

  • 带有ActionListener 的按钮比矩形上的MouseListener 要好得多。
    • 当鼠标指向按钮时,按钮将显示焦点,或者通过键盘切换到组件。
    • 该按钮可通过键盘访问。
    • 该按钮具有支持内置多个图标的功能(例如,用于“初始外观”、聚焦、按下等)
  • 通过添加 EmptyBorder 在菜单面板和游戏周围提供 GUI 中的空白区域
  • 通过设置边距使按钮变大。
  • 根据需要调整边距、边框和首选大小。这些尺寸是我设置的,以免屏幕截图太大。
  • 在代码 cmets 中查看更多提示。

代码

这是生成上述屏幕截图的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://stackoverflow.com/a/7143398/418556 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);
    }
}

【讨论】:

  • 谢谢,我会努力添加这个。那些漂亮的截图你是怎么拍的?
  • “那些漂亮的截图你是怎么拍的?”How do I create a screenshot to illustrate a post
  • 哦,我想我会删除之前的评论,因为你改变了主意。但转载了对你问题的回复。 ;)
  • 哈,所以我正在尝试添加此代码,但我没有复制/粘贴的奢侈。 Grr...需要 wifi 而不是 LTE。无论如何,我有一个错误。它说我在 public void run() 之后需要一个分号。
  • 哦,100k 不错...恭喜
【解决方案2】:

“有没有办法让我将我的 GameMenu jpanel 添加到我的 jframe,然后用 Pipes jpanel 替换它?”

正如其他人所建议的,为此您需要CardLayout。这对你来说很简单。就我个人而言,我总是将我的 CardLayout 包裹在 JPanel 而不是 JFrame 中,这只是习惯的力量。

您想要做的是拥有一个mainPanel,它将拥有CardLayout

CardLayout card = new CardLayout();
JPanel mainPanel = new JPanel();

然后您想将您的面板添加到mainPanelCardLyaout 所做的是layer 面板,一次只显示一个。您添加的第一个将在前台。此外,当您添加面板时,您还需要向它发出一个 key 可以从中调用它。键,可以是任何你喜欢的字符串。

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

现在gameMenu 是唯一显示的面板。要显示pipes,就用这个方法

所以你会使用,card.show(mainPanel, "pipes");

无论您想触发pipes 的显示,只需在该事件处理程序中添加该行即可。您可以向GameMenu 添加一个按钮或其他东西,以允许移动到Pipes 面板。

【讨论】:

  • @peeskillet 芭比娃娃正在热身。那一箱啤酒在哪里? ;) Cyber​​ Storm:这是一个关于我达到 100K 代表的笑话。
  • @AndrewThompson .. 我也能发现牛粪。我们都知道 100k 具有误导性;-D 37 去!
  • @AndrewThompson 99,963 :)
  • @peeskillet 是的。回到 100K。我不得不向你承认,我实际上是为了让滴答声回到我的答案。如果有任何安慰,请了解以下内容。我经常会打开一个问题,看到你的回复,快速浏览它,想“解决了这个问题!”,添加一个赞成票并关闭问题。 :)
  • @AndrewThompson awww
【解决方案3】:

这需要在菜单上单击鼠标。您可以稍后将其更改为单击某个按钮或任何您想要的。

我在Game 类中添加了一个MouseListener。当用户在menu JPanel上按下鼠标时,它会将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框架中显示的矩形内单击鼠标时触发事件?比如当鼠标按下时,获取鼠标的坐标,检查是否在矩形内?
  • 你知道矩形的坐标,所以你可以手动检查。在Rectangle2D 中还有一个contains 方法。看这里:stackoverflow.com/questions/7296438/…
  • 更新了我的答案。它会验证您现在是否在 Rectangle 内单击。
  • 谢谢雨果,这会有所帮助
  • -1 是给我的@Cyber​​Storm。好吧,我接受这可能不是解决问题的最佳方法,但至少它有效。不过,请等待更多答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多