java - 使用 Java 将 Image 设置为 Button 并在 Puzzle Game 中处理 ActionListener

标签 java swing puzzle

我只学习了 Java Swing 1 周,所以我尝试完成一些练习。这是我的代码。我将 9 图标设置为 9 按钮,但它没有显示在按钮上。

package mypack;

import java.awt.Color;

import javax.swing.*;

public class PuzzleGame extends JFrame{
    static JButton bt1,bt2,bt3,bt4,bt5,bt6,bt7,bt8,bt9,btNew,btExit;
    static JLabel move, moveNum, time, timeNum;
    public PuzzleGame(){
        createMyGUI();
    }

    public static void createMyGUI(){
        JFrame jf = new JFrame("Game Puzzle Java");
        JPanel jpl = new JPanel();
        Icon icSpace = new ImageIcon("images/0.png");
        Icon ic1 = new ImageIcon("images/1.png");
        Icon ic2 = new ImageIcon("images/2.png");
        Icon ic3 = new ImageIcon("images/3.png");
        Icon ic4 = new ImageIcon("images/4.png");
        Icon ic5 = new ImageIcon("images/5.png");
        Icon ic6 = new ImageIcon("images/6.png");
        Icon ic7 = new ImageIcon("images/7.png");
        Icon ic8 = new ImageIcon("images/8.png");
        jpl.setSize(100,100);
        jpl.setBounds(480, 50, 200, 200);
        jpl.setBackground(Color.BLUE);
        move = new JLabel("Move:");
        move.setBounds(480,10,50,20);
        moveNum = new JLabel("0");
        moveNum.setBounds(530, 10, 50, 20);
        time = new JLabel("Time:");
        time.setBounds(580, 10, 50, 20);
        timeNum = new JLabel("0");
        timeNum.setBounds(630,10,50,20);
        btNew = new JButton("New Game");
        btNew.setBounds(480, 270, 200, 80);
        btExit = new JButton("Exit");
        btExit.setBounds(480, 370, 200, 80);
        jf.add(move);
        jf.add(moveNum);
        jf.add(time);
        jf.add(timeNum);
        jf.add(btNew);
        jf.add(btExit);
        jf.add(jpl);
        jf.setSize(700, 500);
        jf.setLocation(300,20);
        jf.setLayout(null);
        jf.setResizable(false);
        jf.setVisible(true);
        bt1 = new JButton();
        bt1.setBounds(10, 10, 150, 150);
        bt1.setIcon(ic1);
        bt2 = new JButton();
        bt2.setBounds(160, 10, 150, 150);
        bt2.setIcon(ic2);
        bt3 = new JButton();
        bt3.setBounds(310, 10, 150, 150);
        bt3.setIcon(ic3);
        bt4 = new JButton();
        bt4.setBounds(10, 160, 150, 150);
        bt4.setIcon(ic4);
        bt5 = new JButton();
        bt5.setBounds(160, 160, 150, 150);
        bt5.setIcon(ic5);
        bt6 = new JButton();
        bt6.setBounds(310, 160, 150, 150);
        bt6.setIcon(ic6);
        bt7 = new JButton();
        bt7.setBounds(10, 310, 150, 150);
        bt7.setIcon(ic7);
        bt8 = new JButton();
        bt8.setBounds(160, 310, 150, 150);
        bt8.setIcon(ic8);
        bt9 = new JButton();
        bt9.setBounds(310, 310, 150, 150);
        bt9.setIcon(icSpace);
        jf.add(bt1);
        jf.add(bt2);
        jf.add(bt3);
        jf.add(bt4);
        jf.add(bt5);
        jf.add(bt6);
        jf.add(bt7);
        jf.add(bt8);
        jf.add(bt9);
    }

    public static void main(String[] args){
        PuzzleGame.createMyGUI();
    }   
}

我认为 setIcon 方法不适用于 Button。另外,有人告诉我如何用我的代码在益智游戏中设置一个 Action 将困惑的图片排列成完整的图片。

最佳答案

我在您的代码中发现了一些问题:

  1. 您正在扩展 JFrame 并在类中创建一个新的 JFrame 对象。您永远不会使用您的类(扩展类)的 JFrame。因此,明智的做法是删除它。

    您应该避免扩展JFrame,因为这意味着您的类JFrameJFrame是一个刚性的容器,而不是让您的程序基于 JPanel 并将它们添加到其他容器。引用:Using extends JFrame vs calling it inside of class .

  2. 您已经不再使用 static 关键字。 static不是跨方法传字,它会对你造成很大的伤害,不要再使用它了。相反,创建类的实例并以这种方式调用您的方法。

  3. 您有多个执行相同操作的对象:

    Icon ic1 = new ImageIcon("images/1.png");
    Icon ic2 = new ImageIcon("images/2.png");
    ...
    

    为什么不使用 Icon[] 图标并对其进行迭代?

  4. 您正在使用邪恶的 null 布局setBounds(...),停止使用它,而使用 layout managers以及EmptyBorder s 用于组件之间的额外间距。

    虽然像素完美定位可能是 Swing 新手创建复杂 GUI 的最简单方法,但您使用它的次数越多,您会发现与此相关的问题就越多。 Swing 必须处理不同的平台、屏幕尺寸、分辨率、PLAF 等。这就是为什么像素完美的 GUI 只是一种幻想。引用见Null layout is evil以及 this question 中的答案进一步解释为什么应该避免空布局

  5. 在添加所有组件之前,您要使 JFrame 可见,这可能会导致 GUI 在显示之前无法完全绘制,并可能导致出现“错误”组件只有在将鼠标悬停在其应在的位置时才会显示。 JFrame#setVisible(...)应该是最后调用的行之一。

  6. 您正在调用JFrame#setSize(...) ,您应该覆盖 getPreferredSize您的内部 JPanel ,然后调用 JFrame#pack() ,因此您的 JFrame 将其尺寸减小到所有组件都以其首选尺寸可见的最小尺寸。请参阅Should I avoid the use of setPreferred|Maximum|MinimumSize in Java Swing? (普遍共识是"is")。

  7. 您没有将程序放在 Event Dispatch Thread (EDT) 上,Swing 不是线程安全的,这可能会使您的程序有时卡住,您可以通过更改 main(...) 方法来解决此问题,如下所示:

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                //Your constructor here
            }
        });
    }
    
<小时/>

您的图像可能没有被定位,但是一旦您将程序打包为 JAR 文件,它们就会成为嵌入式资源,因此,明智的做法是开始将文件(或图像)视为已经存在的文件(或图像)。

您可以更改例如:

Icon ic1 = new ImageIcon("images/1.png");

对此:

Icon ic1 = null;
try {
    ic1 = new ImageIcon(ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/1.png")));
} catch (IOException e) {
    e.printStackTrace();
}

这将使您的图像被加载。看这个question以及接受的答案供引用。

这应该可以解决您的问题,并且您的 GUI(我用 2 个图标完成)应该如下所示:

enter image description here

但是如果您想遵循我的上述建议,您可以尝试此代码,它使用布局管理器、空边框、覆盖 getPreferredSize() 方法并使用 pack()等,并生成一个与您已有的非常相似的 GUI:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ImagesInResourcesExample {

    private JFrame frame;

    private JPanel buttonsPane;
    private JPanel rightPane;
    private JPanel scorePanel;
    private JPanel colorPanel;

    private BufferedImage img;

    private JButton[][] buttons;

    private JLabel moveLabel;
    private JLabel timeLabel;

    private JButton newGameButton;
    private JButton exitButton;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ImagesInResourcesExample().createAndShowGui();
            }
        });
    }

    @SuppressWarnings("serial")
    public void createAndShowGui() {
        frame = new JFrame(getClass().getSimpleName());
        buttons = new JButton[3][3];
        moveLabel = new JLabel("Move: 0");
        timeLabel = new JLabel("Time: 0");
        colorPanel = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
        };
        colorPanel.setBackground(Color.BLUE);
        colorPanel.setOpaque(true);

        newGameButton = new JButton("New Game");
        exitButton = new JButton("Exit");

        try {
            img = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/arrow.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        buttonsPane = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(500, 500);
            }
        };
        buttonsPane.setLayout(new GridLayout(3, 3));
        buttonsPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons.length; j++) {
                buttons[i][j] = new JButton(new ImageIcon(img));
                buttonsPane.add(buttons[i][j]);
            }
        }

        rightPane = new JPanel();
        rightPane.setLayout(new GridBagLayout());

        scorePanel = new JPanel();
        scorePanel.setLayout(new GridLayout(1, 2, 10, 10));

        scorePanel.add(moveLabel);
        scorePanel.add(timeLabel);

        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(10, 10, 10, 10);

        rightPane.add(scorePanel, gbc);

        gbc.gridx = 0;
        gbc.gridy = 1;

        rightPane.add(colorPanel, gbc);

        gbc.gridy = 2;
        gbc.ipadx = 30;
        gbc.ipady = 80;

        rightPane.add(newGameButton, gbc);

        gbc.gridy = 3;

        rightPane.add(exitButton, gbc);

        rightPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        frame.add(buttonsPane, BorderLayout.CENTER);
        frame.add(rightPane, BorderLayout.EAST);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

enter image description here

正如你所看到的,代码最多比你已有的代码长 20 行,但是如果你继续向两个程序添加元素,我所做的代码将来会比你的代码短。最终将使用空布局

我希望您遵循上述建议并以这个例子为例并尝试理解它并改进它。

关于java - 使用 Java 将 Image 设置为 Button 并在 Puzzle Game 中处理 ActionListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42443759/

相关文章:

java - JPA中的显式和隐式JOIN有什么区别? (表现)

java - 为什么在调试 JBoss 时 Eclipse 会跳行?

java - 带 HashMap 的准备语句,异常

java - 如何在输入验证期间显示临时气球工具提示?

algorithm - 解码排列的英文字符串

c# - 将 C++ jintArray 传递给 C# 函数

java - 如何从 Swing 应用程序获取 Windows 登录凭据?

java - JTable中使用JCheckBox背景颜色闪烁

java - 数字谜题解决方案的数字平方和可以进一步优化吗?

java - 如何解决这个难题?