Можете ли вы добавить JButtons внутри JPanel

Я делаю игру Морской бой, используя одну доску. На рисунке ниже показано, как должен выглядеть графический интерфейс платы. Мой текущий код устанавливает доску без «слоя меню». (см. рисунок) Я попытался сделать это с помощью конструктора Swing GUI в Intellij и получил следующую форму (см. рисунок), но не могу найти способ вставить эти кнопки внутрь панели. В настоящее время у меня есть следующий код.

package BattleshipGUI;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;



public class BoardFrame extends JFrame {

//variables

Ships ship = new Ships();
static final Color colorHit = Color.red;
static final Color colorNormal = Color.gray;
static final Color colorWater = Color.blue;
static final Color colorCarrier = Color.yellow;
static final Color colorBattleship = Color.BLACK;
static final Color colorSubmarine = Color.magenta;
static final Color colorDestroyer = Color.white;
int[][] map;
private Container contents;
int cols;
int rows;
boolean equalPoints;

Player p1 = new Player();
Player p2 = new Player();
private JPanel mainPanel; // I tried using the GUI swing designer, but I cant manage to create 
                                //a menu-layer on top of the buttons
private JButton buttonHighScores;
private JButton quitGame;
private JLabel player1Points;
private JLabel player2Points;
private JLabel playerTurn;
JButton[][] tiles = new JButton[100][100]; // I create 100*100 buttons, but only assign the right   
                                            // number to them


public void setMap(int[][] map) {
    this.map = map;
}
public void SetFrame(int r, int c) {
    this.rows = r;
    this.cols = c;

}




public BoardFrame(int r, int c) {

    super("Battleship");
    setSize(400,450); // I create a frame
    this.setLayout(new GridLayout(r, c)); // I set the layout depending on the number of rows and 
                                         //columns
    AttackHandler attackHandler = new AttackHandler(); // action event class
    p1.turn = true; // player 1's turn is true, he commences
    p2.turn= false; //player 2's turn is false




    for (int i =0; i<r; i++) {
        for (int j =0; j <c; j++) {
            tiles[i][j] = new JButton();       // this is where all the buttons are declared
            tiles[i][j].setBackground(colorNormal);
            add(tiles[i][j]);
            tiles[i][j].addActionListener(attackHandler);
        }
    }


    setResizable(true);
    setLocationRelativeTo(null);
    setVisible(true);
    setDefaultCloseOperation(this.DISPOSE_ON_CLOSE);
    setSize(400,450);               //the size of my frame


}





public class AttackHandler implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (source == tiles[i][j]) {
                    isHit(i, j);
                    return;
                }
            }
        }
    }
}
public void isHit(int row, int col) { 
       // the map consists of an 2D array 
      //where 0's represent the water, 1's represent the tiles that are 
      //already hit, and the other represent the ships

    if (AllHit()) {
        JOptionPane.showMessageDialog(null, "All Ships Destructed!");
        setWinner();
        String winner =DecideWinner();
        JOptionPane.showMessageDialog(null, winner+" has won!", "Winner", 
 JOptionPane.PLAIN_MESSAGE);
        int again = JOptionPane.showConfirmDialog(null, "Play Again", "Play 
 Again",JOptionPane.YES_NO_OPTION);
        if (again==0){
            Main.main(null);
        }
        dispose();
    }
    else if (map[row][col] == 5) {
        AddPoint(p1.turn, 5); // this should add points to the player that 
                              //has the turn
        tiles[row][col].setBackground(colorCarrier);
        map[row][col] = 1;
    } else if (map[row][col] == 4) {
        AddPoint(p1.turn, 4);
        tiles[row][col].setBackground(colorBattleship);
        map[row][col] = 1;

    } else if (map[row][col] == 3) {
        AddPoint(p1.turn, 3);
        tiles[row][col].setBackground(colorSubmarine);
        map[row][col] = 1;
    } else if (map[row][col] == 2) {
        AddPoint(p1.turn, 2);
        tiles[row][col].setBackground(colorDestroyer);
        map[row][col] = 1;
    } else if (map[row][col] == 1) {
        System.out.println("Already Hit!");
    } else if (map[row][col] == 0 || map[row][col] == 9) {
        tiles[row][col].setBackground(colorWater);
        map[row][col] = 1;
    }
    p1.changeTurn(p1.turn); // every click, the turn of each player should 
                           //switch from true to false, p1 starts with value 
                           //true, and p2 with false
    p2.changeTurn(p2.turn);
    System.out.println("Points Player 1: "+p1.points);
    System.out.println("Points Player 2: "+p2.points);
}


public boolean AllHit() { // if all ships are hit, a message pops up
    boolean allHit = true;
    for (int i = 0; i<rows;i++) {
        for (int j = 0; j < cols; j++) {
            if (map[i][j] != 1 && map[i][j] != 0) {
                allHit = false;
                break;
            }
        }
    }
    return allHit;
}

public void AddRandomShips(int rows, int cols) {      
    int[][] map = ship.setRandomShips(rows, cols);
    setMap(map);
}
public void AddShipsNotRandom(int size, int[][] coordinates){   
    int[][] map = ship.PlaceShips(size, coordinates);
    setMap(map);
}

public void ScoreMethod(boolean scoreMethod){ 
    if (scoreMethod){
        equalPoints = true;
    }
    else if (!scoreMethod){    // the second method adjusts the score 
                             //for the second player because the first player 
                              //is more likely to hit a ship
        equalPoints = false;
    }
}

public void AddPoint(boolean turn,int amount){
    if (turn){
        p1.points = p1.points+ amount;
    }
    else p2.points = p2.points+amount;
}

public void setWinner(){
    if (p1.points>p2.points){
        p1.setWon();
    }
    else p2.setWon();
}

public String DecideWinner(){
    if (p1.won == true){
        return "Player 1";
    }
    else return "Player 2";
}

}

Вот как должен выглядеть графический интерфейс:

Вот что генерирует мой код:

это то, что я разработал с помощью Intellij Swing Designer (панель с «кнопками» должна быть заполнена JButtons, которые я создал в коде):

ОБНОВЛЯТЬ :

Благодаря г-н. Kroukamp, ​​я смог добавить меню-оверлей. Код выглядит следующим образом:

package BattleshipGUI;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import java.awt.Dimension;


public class BoardFrame extends JFrame {

//variables
Ships ship = new Ships();
static final Color colorNormal = Color.gray;
static final Color colorWater = Color.blue;
static final Color colorCarrier = Color.yellow;
static final Color colorBattleship = Color.BLACK;
static final Color colorSubmarine = Color.magenta;
static final Color colorDestroyer = Color.white;
int[][] map;
int cols;
int rows;
boolean equalPoints;
Player p1 = new Player();
Player p2 = new Player();
JButton[][] tiles = new JButton[100][100];

public void setMap(int[][] map) {
    this.map = map;
}
public void SetFrame(int r, int c) {
    this.rows = r;
    this.cols = c;

}

JButton highScoresButton = new JButton("High Scores");
JLabel player1ScoreTitleLabel = new JLabel(String.valueOf(p1.points));
JLabel turnTitleLabel = new JLabel(Turn());
JLabel player2ScoreTitleLabel = new JLabel(String.valueOf(p1.points));
JButton quitGameButton = new JButton("Quite Game");
JFrame frame = new JFrame("Battleship");




public  BoardFrame(int r, int c) {

    super("Battleship");
    AttackHandler attackHandler = new AttackHandler(); // action event class

    p1.turn = true; // player 1's turn is true, he commences
    p2.turn= false; //player 2's turn is false


    JFrame frame = new JFrame("Battleship");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // panel 1 (yellow border)
    JPanel panel1 = new JPanel(new GridBagLayout());
    JButton highScoresButton = new JButton("High Scores");
    JLabel player1ScoreTitleLabel = new JLabel(String.valueOf(p1.points));
    JLabel turnTitleLabel = new JLabel(Turn());
    JLabel player2ScoreTitleLabel = new JLabel(String.valueOf(p1.points));
    JButton quitGameButton = new JButton("Quite Game");
    GridBagConstraints s = new GridBagConstraints();
    s.weightx = 1;
    s.gridx = 0;
    s.gridy = 0;
    panel1.add(highScoresButton, s);
    s.weightx = 1;
    s.gridx = 1;
    s.gridy = 0;
    panel1.add(player1ScoreTitleLabel, s);
    s.weightx = 1;
    s.gridx = 2;
    s.gridy = 0;
    panel1.add(turnTitleLabel);
    s.weightx = 1;
    s.gridx = 3;
    s.gridy = 0;
    panel1.add(player2ScoreTitleLabel, s);
    s.weightx = 1;
    s.gridx = 4;
    s.gridy = 0;
    panel1.add(quitGameButton, s);

    // panel 2 (red border)
    JPanel panel2 = new JPanel();
    GridLayout layout = new GridLayout(r, c);
    layout.setHgap(0);
    layout.setVgap(0);
    panel2.setLayout(layout);
    panel2.setBorder(new LineBorder(Color.blue, 4));
    for (int i =0; i<r; i++) {
        for (int j =0; j < c; j++) {
            tiles[i][j] = new JButton(){
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(100,100);
                    }
                };
            tiles[i][j].setBackground(colorNormal);
            panel2.add(tiles[i][j]);
            tiles[i][j].addActionListener(attackHandler);
            }
        }

    frame.add(panel1, BorderLayout.NORTH);
    frame.add(panel2, BorderLayout.CENTER);
    frame.pack();
    frame.setVisible(true);

}

public class AttackHandler implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (source == tiles[i][j]) {
                    isHit(i, j);
                    return;
                }
            }
        }
    }
}


public void isHit(int row, int col) { 

    if (AllHit()) {
        JOptionPane.showMessageDialog(null, "All Ships Destructed!");
        setWinner();
        String winner =DecideWinner();
        if (p1.won){
            p1.checkHighScore();
        }
        else if (p2.won){
            p2.checkHighScore();
        }

        JOptionPane.showMessageDialog(null, winner, "Winner", 
JOptionPane.PLAIN_MESSAGE);
        int again = JOptionPane.showConfirmDialog(null, "Play Again", "Play 
Again",JOptionPane.YES_NO_OPTION);
        if (again==0){
            Main.main(null);
        }
        frame.dispose();
    }
    else if (map[row][col] == 5) {
        AddPoint(p1.turn, 5); 
        tiles[row][col].setBackground(colorCarrier);
        map[row][col] = 1;
    } else if (map[row][col] == 4) {
        AddPoint(p1.turn, 4);
        tiles[row][col].setBackground(colorBattleship);
        map[row][col] = 1;

    } else if (map[row][col] == 3) {
        AddPoint(p1.turn, 3);
        tiles[row][col].setBackground(colorSubmarine);
        map[row][col] = 1;
    } else if (map[row][col] == 2) {
        AddPoint(p1.turn, 2);
        tiles[row][col].setBackground(colorDestroyer);
        map[row][col] = 1;
    } else if (map[row][col] == 1) {
        System.out.println("Already Hit!");
    } else if (map[row][col] == 0 || map[row][col] == 9) {
        tiles[row][col].setBackground(colorWater);
        map[row][col] = 1;
    }
    p1.changeTurn(p1.turn); 
    p2.changeTurn(p2.turn);
    
    // I cannot find a way to change the labels....
    player1ScoreTitleLabel.setText(String.valueOf(p1.points)); 
    player2ScoreTitleLabel.setText(String.valueOf(p2.points)); 
    turnTitleLabel.setText(Turn());
    System.out.println("Points Player 1: "+p1.points);
    System.out.println("Points Player 2: "+p2.points);
}


public boolean AllHit() {
    boolean allHit = true;
    for (int i = 0; i<rows;i++) {
        for (int j = 0; j < cols; j++) {
            if (map[i][j] != 1 && map[i][j] != 0) {
                allHit = false;
                break;
            }
        }
    }
    return allHit;
}

public void AddRandomShips(int rows, int cols) {        
    int[][] map = ship.setRandomShips(rows, cols);
    setMap(map);
}
public void AddShipsNotRandom(int size, int[][] coordinates){   
    int[][] map = ship.PlaceShips(size, coordinates);
    setMap(map);
}

public void ScoreMethod(boolean scoreMethod){   
    if (scoreMethod){
        equalPoints = true;
    }
    else {          
        equalPoints = false;
    }
}

public void AddPoint(boolean turn,int amount){
    if (turn){
        p1.points = p1.points+ amount;
    }
    else p2.points = p2.points+amount;
}

public void setWinner(){
    if (p1.points>p2.points){
        p1.setWon();
    }
    else if (p2.points>p1.points){
        p2.setWon();
    }
}

public String DecideWinner(){
    if (p1.won){
        return "Player 1 won the game!";
    }
    else if (p2.won){
        return "Player 2 won the game!";
    }
    else return "It's a tie!";
}

public String Turn(){
    if (p1.turn){
        return "Player 1";
    }else return "Player 2";
 }

Это генерирует следующий графический интерфейс:

Тем не менее, я все еще пытаюсь найти способ изменить значения текста оценки JLabels, следующий код у меня не сработал:

    player1ScoreTitleLabel.setText(String.valueOf(p1.points));
    player2ScoreTitleLabel.setText(String.valueOf(p2.points));
    turnTitleLabel.setText(Turn())

Вы должны использовать шаблон MVC и начать с модели. Сначала создайте всю игровую модель и элемент управления (доску, размещение лодок, атакующие квадраты), затем протестируйте ее (создайте метод или класс только для тестирования) и, НАКОНЕЦ, добавьте к ней графический интерфейс (я бы рекомендовал JTable поверх массива JButtons, особенно при щелчке по квадрату, он должен впоследствии отображаться как JLabel)

ControlAltDel 20.12.2020 14:15

Какую отладку вы уже сделали? Какой текст вы помещаете на кнопки, чтобы убедиться, что они отображаются?

AlBlue 20.12.2020 14:17

Я только начал изучать Java на этой неделе, так что извините, что не отвечаю должным образом на ваши вопросы... . Я не знаком с шаблонами MVC, и мне потребовалось бы слишком много времени, чтобы переделать всю структуру моего проекта. @ AIBlue, я использую карту массива [][], которая содержит значения 0 для воды, 1 для плиток, которые уже были ею, 2 для корабля-разрушителя и так далее. Затем JButton[][] сохраняет эти значения. Я добавил больше кода, чтобы показать больше логики.

DrAdler 20.12.2020 14:36

Я настоятельно рекомендую не использовать дизайнера, так как многие из ответов здесь этого не делают, поэтому может быть сложнее интегрировать любой ответ, также вы, вероятно, могли бы использовать JLabels вместо JButtons. Что касается расположения кнопок / меток, я' я рассмотрю возможность использования GridLayout на вашем JPanel. Вот пример шахматной игры, в которой используется аналогичный формат для того, чего вы пытаетесь достичь.

David Kroukamp 20.12.2020 14:46
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
235
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В дополнение к моему комментарию здесь приведен небольшой пример, который поможет вам начать работу без бремени IDE для создания вашего пользовательского интерфейса, включающего как можно больше лучших практик Swing:

  • Я решил использовать JLabels, так как это имеет больше смысла
  • Желтая рамка представляет собой panel1, который использует GridBagLayout.
  • Красная панель представляет собой panel2, в котором используется GridLayout.
  • Наконец, 2 JPanel добавляются к JFrame, который по умолчанию использует BorderLayout.

TestApp.java:

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 javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class TestApp {

    public TestApp() {
        createAndShowGui();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestApp::new);
    }

    private void createAndShowGui() {
        JFrame frame = new JFrame("TestApp");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // panel 1 (yellow border)
        JPanel panel1 = new JPanel(new GridBagLayout());
        JButton highScoresButton = new JButton("High Scores");
        JLabel player1ScoreLabel = new JLabel("<html>Player 1 Score:<br><h2>950</h2>");
        JLabel turnLabel = new JLabel("<html>Turn:<br><h1>Player 1</h1>");
        JLabel player2ScoreLabel = new JLabel("<html>Player 2 Score:<br><h2>925</h2>");
        JButton quitGameButton = new JButton("Quit Game");
        GridBagConstraints c = new GridBagConstraints();
        c.weightx = 1;
        c.gridx = 0;
        c.gridy = 0;
        panel1.add(highScoresButton, c);
        c.weightx = 1;
        c.gridx = 1;
        c.gridy = 0;
        panel1.add(player1ScoreLabel, c);
        c.weightx = 1;
        c.gridx = 2;
        c.gridy = 0;
        panel1.add(turnLabel);
        c.weightx = 1;
        c.gridx = 3;
        c.gridy = 0;
        panel1.add(player2ScoreLabel, c);
        c.weightx = 1;
        c.gridx = 4;
        c.gridy = 0;
        panel1.add(quitGameButton, c);
        panel1.setBorder(new LineBorder(Color.YELLOW, 4)); // just for visual purposes of the answer

        // panel 2 (red border)
        JPanel panel2 = new JPanel();
        GridLayout layout = new GridLayout(8, 8);
        layout.setHgap(5);
        layout.setVgap(5);
        panel2.setLayout(layout);
        panel2.setBorder(new LineBorder(Color.RED, 4)); // just for visual purposes of the answer

        for (int i = 0; i < 64; i++) {
            JLabel label = new JLabel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(100, 100);
                }
            };
            label.setOpaque(true);
            label.setBackground(Color.DARK_GRAY);
            panel2.add(label);
        }

        frame.add(panel1, BorderLayout.NORTH);
        frame.add(panel2, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }
}

@DrAdler Я вижу ваше обновление и предлагаю опубликовать новый вопрос, однако, похоже, ярлык обновляется? Он теряет предыдущий текст, который был установлен на метке, но вы можете просто сделать setText("<html>Player 2 Score:<br><h2>" + String.valueOf(p1.points) + "</h2>");, например, чтобы сохранить предыдущий текст и обновить счет / ход игроков.

David Kroukamp 20.12.2020 19:02

Другие вопросы по теме