简体   繁体   中英

How to Randomize Placement of a JLabel on a GrindLayout of JButtons

I am trying to make a chess board, that will randomize the place of its pieces throughout the board.

Below is what I have so far

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

public class ChessBoard extends JFrame {

    JLayeredPane layeredpane;
    JPanel chessboard;
    JButton[][] chessboardButtons;
    Color black;
    JLabel [][] chessboardLabels;

    UIManager Ui;


    ChessBoard() {
        Dimension Size = new Dimension(600, 600);

        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        pack();
        setResizable(true);
        setLocationRelativeTo(null);
        setVisible(true);
        setSize(600, 600);
        setTitle("Chess Board");

        layeredpane = new JLayeredPane();
        getContentPane().add(layeredpane);
        layeredpane.setPreferredSize(Size);

        chessboard = new JPanel();
        layeredpane.add(chessboard, JLayeredPane.DEFAULT_LAYER);
        chessboard.setLayout(new GridLayout(8, 8));
        chessboard.setPreferredSize(Size);
        chessboard.setBounds(0, 0, Size.width, Size.height);

        Ui = new UIManager();
        chessboardButtons = new JButton[8][8];
        black = Color.black;


        ButtonHandler handler = new ButtonHandler();
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                chessboardButtons[i][j] = new JButton();
                chessboardButtons[i][j].setBorderPainted(false);

                if ((i + j) % 2 != 0) {
                    chessboardButtons[i][j].setBackground(black);
                    chessboardButtons[i][j].setOpaque(true);
                }
                chessboard.add(chessboardButtons[i][j]);
                chessboardButtons[i][j].addActionListener(handler);

            }
        }

        chessboardLabels = new JLabel[8][8];
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                chessboardLabels[i][j] = new JLabel();
                chessboardLabels[i][j].setFont(new Font("Ariel", Font.BOLD, 20));
                chessboardLabels[i][j].setText("H");
                chessboardLabels[i][j].setHorizontalAlignment(JLabel.CENTER);
                chessboardLabels[i][j].setVerticalAlignment(JLabel.CENTER);
                chessboardLabels[i][j].setOpaque(true);
                chessboardButtons[i][j].add(chessboardLabels[i][j]);

                if(chessboardButtons[i][j].getBackground() == Color.black) {
                    chessboardLabels[i][j].setBackground(Color.black);
                    chessboardLabels[i][j].setForeground(Color.white);
                }

            }
        }









    }


    private class ButtonHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == chessboardButtons[0][0]) {
                System.out.println("Button 0,0");
            }
            if (e.getSource() == chessboardButtons[0][1]) {
                System.out.println("Button 0,1");
            }

        }
    }
}

Right now I have the letter H filling all my buttons. What I need it to do is to 1) limit the number of "H"'s there are on the board to be 32, 16 "White" and 16 "black" and 2) randomize the placement throughout the board.

Any ideas will be helpful!

I tried exactly this years ago, and I ran into several problems. One of the biggest is that dragging a piece from one square (JComponent) to another hard to do, because each JComponent has its bounds, and its Graphics clipping prevents you from drawing outside of these bounds. There are workarounds, like adding an Image to a higher level in the JLayeredPane, but this is still very hard to get right.

You really want to make you GUI a custom JComponent/JPanel that draws the whole board and can get mouse events across the whole board.

But before that, the right place to start is by creating a ChessModel that encapsulates the logic of the game. If you do that first and test it thoroughly, adding a GUI on top of that is easier than the other way around.

public class ChessModel {
  char[][] board = new char[8][8];
  
  ...
  public Point[] getPossibleMoves(Point pieceLocation) {
   ...
  }
}
  1. Create an ArrayList with 64 Integers numbered from 0 - 63
  2. Use Collections.shuffle(...) to shuffle the numbers randomly
  3. Take the first 16 values from the ArrayList and add white pieces to the board based on the integer value.
  4. Take the next 16 values from the ArrayList and add the black pieces to the board.

First, I would start by decoupling parts of the system, this will provide you with a lot more flexibility.

For example, you want to decouple the "visual" from the "virtual". Components aren't managed in a "grid", instead, they are maintained in a list, so, you want some way you can quickly and easily ascertain the location of various components on the screen and how the relate to the "virtual" concept of the game or grid.

This is at the core concept of "model-view-controller", where the model represents a "grid" of "pieces" and the view is used to visually represent the model to the user. So you end up with a little bit of translation going on.

Now, you can do something like...

int row = (value / 8);
int col = (value % 8);

which, given a component index, will give you the row/col that they represent, but I'm also lazy, so, I'm going to isolate the concept of a Piece ...

public class Piece extends JPanel {

    private JLabel label;
    private Point cell;

    public Piece(int index) {
        setLayout(new GridBagLayout());
        label = new JLabel(Integer.toString(index));
        label.setForeground(Color.RED);
        add(label);
        
        setOpaque(false);
    }

    public void setCell(Point cell) {
        this.cell = cell;
    }

    public Point getCell() {
        return cell;
    }
    
}

This does several things for me, first, it gives me a simple building block which can be used to represent a piece of data as well as maintain the "virtual" position of the piece, so I can easily look it up, independently.

It also divorces the piece from the board, which will make it easier to maintain (IMHO).

Next, I build the board...

setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
    for (int col = 0; col < 8; col++) {
        JPanel panel = new JPanel(new GridBagLayout()) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(50, 50);
            }
        };
        // Indexed via x/y
        cells[col][row] = panel;
        if (index % 2 == 0) {
            panel.setBackground(Color.WHITE);
        } else {
            panel.setBackground(Color.BLACK);
        }
        add(panel);
        index++;
    }
    index++;
}

There's nothing really fancy here, it's just GridLayout with a bunch of color panels laid out on it.

The "fancy" part is the idea that, instead of using something complicated, like JLayeredPane , I'm simply going to add the Piece s directly to each cell.

Which leads us to the heart of the problem, how to randomise the position of the cells. Essentially, I'm going to create a list of numbers from 0 to 63 inclusive, randomise the list and then pop each number of off the list till I'm done.

Now, you could use an array, but filling an array with random numbers is not a simple task (especially if you want to guarantee uniqueness )

// Fill a list of numbers
int totalCells = 8 * 8;
List<Integer> locations = new ArrayList<>(totalCells);
for (int value = 0; value < totalCells; value++) {
    locations.add(value);
}
// Randomise the list
Collections.shuffle(locations);

// For al the white pieces, randomise their positions
for (index = 0; index < 16; index++) {
    int value = locations.remove(0);

    // Virtual coordinates
    int row = (value / 8);
    int col = (value % 8);

    Point cell = new Point(col, row);
    Piece piece = new Piece(index);
    whitePieces[index] = piece;
    piece.setCell(cell);

    // Get the component offset by the value (physical)
    JPanel cellPane = (JPanel) getComponent(value);
    cellPane.add(piece);
}

// Now you can continue with the black pieces, just like above
// and because you've removed the "used" cell indexes from the
// list, you won't end up with duplicate positions

Now you're probably scratching your head over all this. But simply put, when you want to move a "piece", you simply remove it from it's current parent container, calculate the position ( (row * 8) + col ), get the new parent component (like above) and add it, simple.

随机的

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Test extends JFrame {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel[][] cells = new JPanel[8][8];
        private Piece[] whitePieces = new Piece[16];

        public TestPane() {
            setLayout(new GridLayout(8, 8));
            int index = 0;
            for (int row = 0; row < 8; row++) {
                for (int col = 0; col < 8; col++) {
                    JPanel panel = new JPanel(new GridBagLayout()) {
                        @Override
                        public Dimension getPreferredSize() {
                            return new Dimension(50, 50);
                        }
                    };
                    // Indexed via x/y
                    cells[col][row] = panel;
                    if (index % 2 == 0) {
                        panel.setBackground(Color.WHITE);
                    } else {
                        panel.setBackground(Color.BLACK);
                    }
                    add(panel);
                    index++;
                }
                index++;
            }

            int totalCells = 8 * 8;
            List<Integer> locations = new ArrayList<>(totalCells);
            for (int value = 0; value < totalCells; value++) {
                locations.add(value);
            }
            Collections.shuffle(locations);

            for (index = 0; index < 16; index++) {
                int value = locations.remove(0);

                int row = (value / 8);
                int col = (value % 8);

                Point cell = new Point(col, row);
                Piece piece = new Piece(index);
                whitePieces[index] = piece;
                piece.setCell(cell);

                JPanel cellPane = (JPanel) getComponent(value);
                cellPane.add(piece);
            }
        }
    }

    public class Piece extends JPanel {

        private JLabel label;
        private Point cell;

        public Piece(int index) {
            setLayout(new GridBagLayout());
            label = new JLabel(Integer.toString(index));
            label.setForeground(Color.RED);
            add(label);

            setOpaque(false);
        }

        public void setCell(Point cell) {
            this.cell = cell;
        }

        public Point getCell() {
            return cell;
        }

    }
}

Oh, and just in case it's important, you can make use of component based drag-n-drop as well

But, needs drives wants

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM