如何在更改背景并使它们可见的同时在 JPanels 上添加 JPanels
how to add JPanels on JPanels while changing the background and making them visible
我正在尝试在 java 中使用 A.I 制作井字游戏,但我是 Java 的 Swing 和图形新手。首先,即使我设置了不透明 true
(请参阅下面的 class ttt
),我也无法更改 JPanel 的背景颜色。然而,我能够更改 JFrame 的背景(参见 class tttFrame
)。其次,当我尝试将 JPanels(这些面板是 X 和 O 所在的位置)添加到我当前 JPanel 的顶部时 - 带有线条的那个(或者可能是,但 JPanels 的背景颜色不会改变) 他们不会显示。 testpanel
和 tttGrid
中的面板都没有显示。我假设我对如何实现 JFrames 和 JPanels 有错误的想法。
我的代码还在 this.add(tttGrid[r][c]);
处抛出空指针异常。 tttGrid
基本上是 JPanel 的二维数组,当被 user/computer 单击时,这些 JPanel 旨在显示 X 或 O。我不确定我的代码如何导致空指针异常。正如您在 this.add(tttGrid[r][c]);
之前的行中注意到的那样,我做了一些测试,我在控制台上得到了这个输出 window:
actual label, row 0 col 0
actual label, row 0 col 1
actual label, row 0 col 2
null label, row 1 col 0
null label, row 1 col 1
null label, row 1 col 2
null label, row 2 col 0
null label, row 2 col 1
null label, row 2 col 2
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class ttt extends JPanel{
private char playerChoice;
private JPanel[][] tttGrid;
public ttt(){
//this.setOpaque(true); Why isn't setBackground working in JPanel and
//this.setBackground(Color.BLUE); only working in JFrame class?
this.setPreferredSize(new Dimension(500,500));
this.setFocusable(true);
tttGrid = new JPanel[3][3];
for(int y = 50; y <= 290; y+=120){
int r = 0;
int c = 0;
for(int x = 70; x <= 310; x+=120){
tttGrid[r][c] = new JPanel();
tttGrid[r][c].setBounds(x,y,120,120);
tttGrid[r][c].setBackground(Color.GREEN);
tttGrid[r][c].setOpaque(true);
c++;
}
r++;
}
for(int r = 0; r < 3; r++){
for(int c = 0; c < 3; c++){
if(tttGrid[r][c] == null){
System.out.println("null panel, row " + r + " col " + c);
}
else{
System.out.println("real panel, row " + r + " col " + c);
}
this.add(tttGrid[r][c]); //nullpointerexception
}
}
//testing if JPanel is visible
JPanel testpanel = new JPanel();
testpanel.setBounds(70,50,120,120);
testpanel.setBackground(Color.MAGENTA);
testpanel.setOpaque(true);
this.add(testpanel);
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(153, 250, 255));
g2d.setStroke(new BasicStroke(5));
g2d.drawLine(70, 170, 430, 170);
g2d.drawLine(70, 290, 430, 290);
g2d.drawLine(190, 50, 190, 410);
g2d.drawLine(310, 50, 310, 410);
}
public void gameStart(){
}
public void drawXO(){
}
}
import javax.swing.*;
import java.awt.*;
public class tttFrame extends JFrame{
public tttFrame(){
this.getContentPane().add(new ttt());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Tic-Tac-Toe");
ImageIcon icon = new ImageIcon("ttt.png");
this.setIconImage(icon.getImage());
this.setBackground(new Color(0,225,237)); //Background color only works on JFrame
this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
new tttFrame();
}
}
下图是我注释掉代码得到的this.add(tttGrid[r][c]);
所以,几个复杂的问题。
首先,您的 NPE 是由于您在复合 for-loop
s
的每次迭代中重置 r
值造成的
for (int y = 50; y <= 290; y += 120) {
int r = 0;
int c = 0;
for (int x = 70; x <= 310; x += 120) {
tttGrid[r][c] = new JPanel();
tttGrid[r][c].setBounds(x, y, 120, 120);
tttGrid[r][c].setBackground(Color.GREEN);
tttGrid[r][c].setOpaque(true);
c++;
}
r++;
}
在这种情况下,r
总是 0
接下来,您将与面板的布局管理器发生冲突。 JPanel
默认使用 FlowLayout
。在这种情况下设置组件的 bounds
将没有任何效果,并且 FlowLayout
管理器将尝试使用组件 preferredSize
来确定应该如何最佳布局。
这会给您带来不希望的结果,预计...
接下来,您将与绘画子系统作斗争。
通过覆盖 paint
,您将完全控制组件及其子组件的绘制方式。如果你不想那么多控制,你应该先调用 super.paint
以确保油漆链得到维护。
但是,作为一般规则,您真的应该考虑覆盖 paintComponent
(只是不要忘记先调用 super.paintComponent
)
看看:
了解更多详情。
可运行示例
以下示例使用了 GridBagLayout
。这是一个更复杂的布局管理器,您可以使用 GridLayout
获得类似的结果,但我喜欢 GridBagLayout
为我提供的灵活性。
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new tttFrame();
}
});
}
public class tttFrame extends JFrame {
public tttFrame() {
this.getContentPane().add(new ttt());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Tic-Tac-Toe");
// ImageIcon icon = new ImageIcon("ttt.png");
// this.setIconImage(icon.getImage());
this.setBackground(new Color(0, 225, 237)); //Background color only works on JFrame
// this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
public class ttt extends JPanel {
private char playerChoice;
private JPanel[][] tttGrid;
public ttt() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
tttGrid = new JPanel[3][3];
gbc.gridy = 0;
gbc.fill = gbc.BOTH;
gbc.insets = new Insets(5, 5, 5, 5);
for (int row = 0; row < 3; row++) {
gbc.gridx = 0;
for (int col = 0; col < 3; col++) {
CellPanel panel = new CellPanel();
add(panel, gbc);
tttGrid[row][col] = panel;
gbc.gridx += 1;
}
gbc.gridy += 1;
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(153, 250, 255));
g2d.setStroke(new BasicStroke(5));
int cellSize = 120 + 10;
int boardSize = cellSize * 3;
int x = (getWidth() / 2) - (cellSize / 2);
int y = (getHeight() / 2) - (boardSize / 2);
g2d.drawLine(x, y, x, y + boardSize);
x += cellSize;
g2d.drawLine(x, y, x, y + boardSize);
x = (getWidth() - boardSize) / 2;
y = (getHeight() / 2) - (cellSize / 2);
g2d.drawLine(x, y, x + boardSize, y);
y += cellSize;
g2d.drawLine(x, y, x + boardSize, y);
}
public void gameStart() {
}
public void drawXO() {
}
}
// You don't "need" to do this, but it ensures that each instance
// is always the same
public class CellPanel extends JPanel {
public CellPanel() {
setBackground(Color.GREEN);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
}
}
这个例子做的一件事是演示布局管理器的灵活性,例如,window 现在可以重新调整大小
随着 window 调整大小,您可以轻松地让单元格填充所有可用的 space,但是,这取决于您
我正在尝试在 java 中使用 A.I 制作井字游戏,但我是 Java 的 Swing 和图形新手。首先,即使我设置了不透明 true
(请参阅下面的 class ttt
),我也无法更改 JPanel 的背景颜色。然而,我能够更改 JFrame 的背景(参见 class tttFrame
)。其次,当我尝试将 JPanels(这些面板是 X 和 O 所在的位置)添加到我当前 JPanel 的顶部时 - 带有线条的那个(或者可能是,但 JPanels 的背景颜色不会改变) 他们不会显示。 testpanel
和 tttGrid
中的面板都没有显示。我假设我对如何实现 JFrames 和 JPanels 有错误的想法。
我的代码还在 this.add(tttGrid[r][c]);
处抛出空指针异常。 tttGrid
基本上是 JPanel 的二维数组,当被 user/computer 单击时,这些 JPanel 旨在显示 X 或 O。我不确定我的代码如何导致空指针异常。正如您在 this.add(tttGrid[r][c]);
之前的行中注意到的那样,我做了一些测试,我在控制台上得到了这个输出 window:
actual label, row 0 col 0
actual label, row 0 col 1
actual label, row 0 col 2
null label, row 1 col 0
null label, row 1 col 1
null label, row 1 col 2
null label, row 2 col 0
null label, row 2 col 1
null label, row 2 col 2
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class ttt extends JPanel{
private char playerChoice;
private JPanel[][] tttGrid;
public ttt(){
//this.setOpaque(true); Why isn't setBackground working in JPanel and
//this.setBackground(Color.BLUE); only working in JFrame class?
this.setPreferredSize(new Dimension(500,500));
this.setFocusable(true);
tttGrid = new JPanel[3][3];
for(int y = 50; y <= 290; y+=120){
int r = 0;
int c = 0;
for(int x = 70; x <= 310; x+=120){
tttGrid[r][c] = new JPanel();
tttGrid[r][c].setBounds(x,y,120,120);
tttGrid[r][c].setBackground(Color.GREEN);
tttGrid[r][c].setOpaque(true);
c++;
}
r++;
}
for(int r = 0; r < 3; r++){
for(int c = 0; c < 3; c++){
if(tttGrid[r][c] == null){
System.out.println("null panel, row " + r + " col " + c);
}
else{
System.out.println("real panel, row " + r + " col " + c);
}
this.add(tttGrid[r][c]); //nullpointerexception
}
}
//testing if JPanel is visible
JPanel testpanel = new JPanel();
testpanel.setBounds(70,50,120,120);
testpanel.setBackground(Color.MAGENTA);
testpanel.setOpaque(true);
this.add(testpanel);
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(153, 250, 255));
g2d.setStroke(new BasicStroke(5));
g2d.drawLine(70, 170, 430, 170);
g2d.drawLine(70, 290, 430, 290);
g2d.drawLine(190, 50, 190, 410);
g2d.drawLine(310, 50, 310, 410);
}
public void gameStart(){
}
public void drawXO(){
}
}
import javax.swing.*;
import java.awt.*;
public class tttFrame extends JFrame{
public tttFrame(){
this.getContentPane().add(new ttt());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Tic-Tac-Toe");
ImageIcon icon = new ImageIcon("ttt.png");
this.setIconImage(icon.getImage());
this.setBackground(new Color(0,225,237)); //Background color only works on JFrame
this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
new tttFrame();
}
}
下图是我注释掉代码得到的this.add(tttGrid[r][c]);
所以,几个复杂的问题。
首先,您的 NPE 是由于您在复合 for-loop
s
r
值造成的
for (int y = 50; y <= 290; y += 120) {
int r = 0;
int c = 0;
for (int x = 70; x <= 310; x += 120) {
tttGrid[r][c] = new JPanel();
tttGrid[r][c].setBounds(x, y, 120, 120);
tttGrid[r][c].setBackground(Color.GREEN);
tttGrid[r][c].setOpaque(true);
c++;
}
r++;
}
在这种情况下,r
总是 0
接下来,您将与面板的布局管理器发生冲突。 JPanel
默认使用 FlowLayout
。在这种情况下设置组件的 bounds
将没有任何效果,并且 FlowLayout
管理器将尝试使用组件 preferredSize
来确定应该如何最佳布局。
这会给您带来不希望的结果,预计...
接下来,您将与绘画子系统作斗争。
通过覆盖 paint
,您将完全控制组件及其子组件的绘制方式。如果你不想那么多控制,你应该先调用 super.paint
以确保油漆链得到维护。
但是,作为一般规则,您真的应该考虑覆盖 paintComponent
(只是不要忘记先调用 super.paintComponent
)
看看:
了解更多详情。
可运行示例
以下示例使用了 GridBagLayout
。这是一个更复杂的布局管理器,您可以使用 GridLayout
获得类似的结果,但我喜欢 GridBagLayout
为我提供的灵活性。
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new tttFrame();
}
});
}
public class tttFrame extends JFrame {
public tttFrame() {
this.getContentPane().add(new ttt());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Tic-Tac-Toe");
// ImageIcon icon = new ImageIcon("ttt.png");
// this.setIconImage(icon.getImage());
this.setBackground(new Color(0, 225, 237)); //Background color only works on JFrame
// this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
public class ttt extends JPanel {
private char playerChoice;
private JPanel[][] tttGrid;
public ttt() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
tttGrid = new JPanel[3][3];
gbc.gridy = 0;
gbc.fill = gbc.BOTH;
gbc.insets = new Insets(5, 5, 5, 5);
for (int row = 0; row < 3; row++) {
gbc.gridx = 0;
for (int col = 0; col < 3; col++) {
CellPanel panel = new CellPanel();
add(panel, gbc);
tttGrid[row][col] = panel;
gbc.gridx += 1;
}
gbc.gridy += 1;
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(153, 250, 255));
g2d.setStroke(new BasicStroke(5));
int cellSize = 120 + 10;
int boardSize = cellSize * 3;
int x = (getWidth() / 2) - (cellSize / 2);
int y = (getHeight() / 2) - (boardSize / 2);
g2d.drawLine(x, y, x, y + boardSize);
x += cellSize;
g2d.drawLine(x, y, x, y + boardSize);
x = (getWidth() - boardSize) / 2;
y = (getHeight() / 2) - (cellSize / 2);
g2d.drawLine(x, y, x + boardSize, y);
y += cellSize;
g2d.drawLine(x, y, x + boardSize, y);
}
public void gameStart() {
}
public void drawXO() {
}
}
// You don't "need" to do this, but it ensures that each instance
// is always the same
public class CellPanel extends JPanel {
public CellPanel() {
setBackground(Color.GREEN);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
}
}
这个例子做的一件事是演示布局管理器的灵活性,例如,window 现在可以重新调整大小
随着 window 调整大小,您可以轻松地让单元格填充所有可用的 space,但是,这取决于您