问题
I'm making a Battleship game using one board. The picture below shows how the board GUI should look. My current code sets up a board without the 'menu-layer'. (see picture) I tried doing this using Swing GUI designer in Intellij and got the following form (see picture), but can't find a way to insert these buttons inside the panel. Currently, I have got the following code.
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";
}
}
This is how the GUI should look like:
This is what my code generates:
this is the for that I designed using Intellij's Swing Designer (the panel with 'buttons' should be filled with the JButton
s I created in the code):
UPDATE :
Thanks to mr. Kroukamp, I was able to add a menu-overlay. The code looks like this:
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";
}
This generates the following GUI:
However, I am still struggling finding a way to change the values of the text of the score JLabels, the following code did not work for me:
player1ScoreTitleLabel.setText(String.valueOf(p1.points));
player2ScoreTitleLabel.setText(String.valueOf(p2.points));
turnTitleLabel.setText(Turn())
回答1:
Adding to my comment here is a small example to help get you started without the burden of an IDE for building your UI incorporating as much of Swing best practices as possible:
- I opted to use
JLabel
s as it just makes more sense - The yellow border represents
panel1
which makes use of a GridBagLayout. - The red panel represents
panel2
which makes use of a GridLayout. - Finally the 2
JPanel
s are added to aJFrame
which by default uses 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);
}
}
来源:https://stackoverflow.com/questions/65380197/can-you-add-jbuttons-inside-a-jpanel