java - 制作一个健壮的、可调整大小的 Swing 棋图形用户界面

标签 java swing user-interface layout-manager

我将如何制作这个可调整大小的国际象棋 GUI?


我们公司的任务是制作国际象棋游戏。它需要在 Windows、OS X 和 Linux/Unix 机器上工作,我们选择了 Java 来实现这一点,同时保持一个通用的代码库(便于维护和降低成本)。

我的任务是创建 GUI。用户设计团队已通过以下规范。与客户。

The chess game (Chess Champ) will be robust to resizing and straightforward, it includes:

  • A tool-bar at the top, with UI components:
    • New button
    • Save button
    • Restore button
    • Resign button
    • A label for providing messages to the player.

On the left hand side of the game, we need an area which will be reserved for future use, it might include things like:

  • Lists of captured pieces
  • A selector for choice of piece when promoting pawns
  • Game statistics
  • Hints, etc.

The details of this are are still being settled with the client and the Logic Team. So for the moment, simply mark it with a label containing ? as text.

The rest of the GUI will consist of the chess board itself. It will have:

  • The main area for the chess board. If the user points at a chess piece, it should show focus with a border. It should also be keyboard accessible. The client will be supplying multiple sprite sheets of chess pieces (of a variety of sizes, styles and colors) to allow the user to change the look of the game.
  • The chess board will have labels indicating the columns (left to right: A, B, C, D, E, F, G & H) and rows (top to bottom: 8, 7, 6, 5, 4, 3, 2 & 1).
  • The chess board and column/row labels will be bordered by a 1px black border, with an 8px padding around that.
  • As the player increases the size of the game, the chess board should remain square, but otherwise fill the available space.
  • The background color behind the chess board should be ochre, but in the mock-ups below we have made the area behind the chess board green in order to highlight the resize behavior.

国际象棋冠军在比赛开始前的最小尺寸

ChessChamp at minimum size, before a game is started

激活新游戏按钮后,最小尺寸的国际象棋冠军

Chess Champ at minimum size, after the new game button is activated

Chess Champ 拉伸(stretch)得比最小尺寸更宽

ChessChamp stretched wider than minimum size

Chess Champ 拉伸(stretch)的高度超过了最小尺寸

Chess Champ stretched taller than minimum size

最佳答案

注意事项

  • 由 9x9 GridLayout 提供左侧和上方各列的棋盘。网格布局的第一个单元格是一个没有文本的标签。

  • 不过,为了简化游戏逻辑,我们维护了一个单独的 8x8 按钮数组。

  • 为了实现键盘功能,我们在棋盘位置使用按钮。这也提供了内置的焦点指示。按钮的边距被移除以允许它们缩小到图标的大小。我们可以给按钮添加一个ActionListener,它会同时响应键盘和鼠标事件。

  • 为了保持方板,我们使用了一些小技巧。棋盘作为唯一未指定 GridBagContraints 的组件添加到 GridBagLayout。这样它总是居中。为了获得所需的调整大小行为,棋盘会查询 父组件的实际大小, 并返回它可以最大的首选大小,同时仍然是正方形并且不超过宽度的较小大小或 parent 的高度。

  • 棋子图片来自Example images for code and mark-up Q&As ,它又是由 'Fill' Unicode characters in labels 开发的.

    使用图像更简单,而填充 Unicode 字符更通用并且“更轻”。 IE。要支持 3 种不同尺寸的 3 种不同棋子样式的 4 种不同颜色,需要 36 个独立的 Sprite 表!


import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.net.URL;
import javax.imageio.ImageIO;

public class ChessGUI {

    private final JPanel gui = new JPanel(new BorderLayout(3, 3));
    private JButton[][] chessBoardSquares = new JButton[8][8];
    private Image[][] chessPieceImages = new Image[2][6];
    private JPanel chessBoard;
    private final JLabel message = new JLabel(
            "Chess Champ is ready to play!");
    private static final String COLS = "ABCDEFGH";
    public static final int QUEEN = 0, KING = 1,
            ROOK = 2, KNIGHT = 3, BISHOP = 4, PAWN = 5;
    public static final int[] STARTING_ROW = {
        ROOK, KNIGHT, BISHOP, KING, QUEEN, BISHOP, KNIGHT, ROOK
    };
    public static final int BLACK = 0, WHITE = 1;

    ChessGUI() {
        initializeGui();
    }

    public final void initializeGui() {
        // create the images for the chess pieces
        createImages();

        // set up the main GUI
        gui.setBorder(new EmptyBorder(5, 5, 5, 5));
        JToolBar tools = new JToolBar();
        tools.setFloatable(false);
        gui.add(tools, BorderLayout.PAGE_START);
        Action newGameAction = new AbstractAction("New") {

            @Override
            public void actionPerformed(ActionEvent e) {
                setupNewGame();
            }
        };
        tools.add(newGameAction);
        tools.add(new JButton("Save")); // TODO - add functionality!
        tools.add(new JButton("Restore")); // TODO - add functionality!
        tools.addSeparator();
        tools.add(new JButton("Resign")); // TODO - add functionality!
        tools.addSeparator();
        tools.add(message);

        gui.add(new JLabel("?"), BorderLayout.LINE_START);

        chessBoard = new JPanel(new GridLayout(0, 9)) {

            /**
             * Override the preferred size to return the largest it can, in
             * a square shape.  Must (must, must) be added to a GridBagLayout
             * as the only component (it uses the parent as a guide to size)
             * with no GridBagConstaint (so it is centered).
             */
            @Override
            public final Dimension getPreferredSize() {
                Dimension d = super.getPreferredSize();
                Dimension prefSize = null;
                Component c = getParent();
                if (c == null) {
                    prefSize = new Dimension(
                            (int)d.getWidth(),(int)d.getHeight());
                } else if (c!=null &&
                        c.getWidth()>d.getWidth() &&
                        c.getHeight()>d.getHeight()) {
                    prefSize = c.getSize();
                } else {
                    prefSize = d;
                }
                int w = (int) prefSize.getWidth();
                int h = (int) prefSize.getHeight();
                // the smaller of the two sizes
                int s = (w>h ? h : w);
                return new Dimension(s,s);
            }
        };
        chessBoard.setBorder(new CompoundBorder(
                new EmptyBorder(8,8,8,8),
                new LineBorder(Color.BLACK)
                ));
        // Set the BG to be ochre
        Color ochre = new Color(204,119,34);
        chessBoard.setBackground(ochre);
        JPanel boardConstrain = new JPanel(new GridBagLayout());
        boardConstrain.setBackground(ochre);
        boardConstrain.add(chessBoard);
        gui.add(boardConstrain);

        // create the chess board squares
        Insets buttonMargin = new Insets(0, 0, 0, 0);
        for (int ii = 0; ii < chessBoardSquares.length; ii++) {
            for (int jj = 0; jj < chessBoardSquares[ii].length; jj++) {
                JButton b = new JButton();
                b.setMargin(buttonMargin);
                // our chess pieces are 64x64 px in size, so we'll
                // 'fill this in' using a transparent icon..
                ImageIcon icon = new ImageIcon(
                        new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB));
                b.setIcon(icon);
                if ((jj % 2 == 1 && ii % 2 == 1)
                        //) {
                        || (jj % 2 == 0 && ii % 2 == 0)) {
                    b.setBackground(Color.WHITE);
                } else {
                    b.setBackground(Color.BLACK);
                }
                chessBoardSquares[jj][ii] = b;
            }
        }

        /*
         * fill the chess board
         */
        chessBoard.add(new JLabel(""));
        // fill the top row
        for (int ii = 0; ii < 8; ii++) {
            chessBoard.add(
                    new JLabel(COLS.substring(ii, ii + 1),
                    SwingConstants.CENTER));
        }
        // fill the black non-pawn piece row
        for (int ii = 0; ii < 8; ii++) {
            for (int jj = 0; jj < 8; jj++) {
                switch (jj) {
                    case 0:
                        chessBoard.add(new JLabel("" + (9-(ii + 1)),
                                SwingConstants.CENTER));
                    default:
                        chessBoard.add(chessBoardSquares[jj][ii]);
                }
            }
        }
    }

    public final JComponent getGui() {
        return gui;
    }

    private final void createImages() {
        try {
            URL url = new URL("http://i.stack.imgur.com/memI0.png");
            BufferedImage bi = ImageIO.read(url);
            for (int ii = 0; ii < 2; ii++) {
                for (int jj = 0; jj < 6; jj++) {
                    chessPieceImages[ii][jj] = bi.getSubimage(
                            jj * 64, ii * 64, 64, 64);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    /**
     * Initializes the icons of the initial chess board piece places
     */
    private final void setupNewGame() {
        message.setText("Make your move!");
        // set up the black pieces
        for (int ii = 0; ii < STARTING_ROW.length; ii++) {
            chessBoardSquares[ii][0].setIcon(new ImageIcon(
                    chessPieceImages[BLACK][STARTING_ROW[ii]]));
        }
        for (int ii = 0; ii < STARTING_ROW.length; ii++) {
            chessBoardSquares[ii][1].setIcon(new ImageIcon(
                    chessPieceImages[BLACK][PAWN]));
        }
        // set up the white pieces
        for (int ii = 0; ii < STARTING_ROW.length; ii++) {
            chessBoardSquares[ii][6].setIcon(new ImageIcon(
                    chessPieceImages[WHITE][PAWN]));
        }
        for (int ii = 0; ii < STARTING_ROW.length; ii++) {
            chessBoardSquares[ii][7].setIcon(new ImageIcon(
                    chessPieceImages[WHITE][STARTING_ROW[ii]]));
        }
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                ChessGUI cg = new ChessGUI();

                JFrame f = new JFrame("ChessChamp");
                f.add(cg.getGui());
                // 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();
                // ensures the minimum size is enforced.
                f.setMinimumSize(f.getSize());
                f.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency
        SwingUtilities.invokeLater(r);
    }
}

关于java - 制作一个健壮的、可调整大小的 Swing 棋图形用户界面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21142686/

相关文章:

java - 有什么理由不应该将我的 keystore 存储在版本控制中?

java - 用Java swing画一个简单的圆不起作用

java - 如何在DesignGridLayout库中动态创建JButton值?

swing - 如何在 Scala Swing 中设置按钮的大小?

java - 如何使用 iBatis

java - 在 android 中退出 NavUtils 的应用程序

java - Spring boot 中 jQuery AJAX 请求出现 404

java - NetBeans 中的文本字段禁用

java - 在 java 面板上绘图 - 没有出现任何内容

c++ - 带 map 的最短路径查找器 C++ GUI