JLabel无法显示图像

5
我正在使用Java创建一个井字棋游戏。现在,当您单击按钮时,该JButton将从JPanel中删除,包含X或O图像的JLabel将被添加,并且JPanel将被重新绘制。然而,当我点击按钮时,图像不会显示,但按钮会消失。
按钮和JLabel/Image的创建:
package tictactoe;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.ImageIcon;

public class TicTacToe implements ActionListener
{
private JFrame holder = new JFrame();
private GridLayout layout = new GridLayout(3,3);
private FlowLayout panel = new FlowLayout(FlowLayout.CENTER);
private JPanel p11, p12, p13, p21, p22, p23, p31, p32, p33;
private JButton b1, b2, b3, b4, b5, b6, b7, b8, b9;
private ImageIcon iconX = new ImageIcon("iconX.png");
private JLabel xLabel = new JLabel(iconX);
private ImageIcon iconO = new ImageIcon("iconO.png");
private JLabel oLabel = new JLabel(iconO);
private int turn;
private char s1, s2, s3, s4, s5, s6, s7, s8, s9;

public TicTacToe()
{
    paint();
}

private void paint()
{
    holder.setLayout(layout);
    holder.setSize(300,300);

    b1 = new JButton("1");
    p11 = new JPanel();
    p11.setLayout(panel);
    p11.add(b1);
    holder.add(p11);

    //Same block of code for the next 8 buttons/panels inserted here

    holder.setVisible(true);

    b1.addActionListener(this);
    //Other action listeners inserted here

}
@Override
public void actionPerformed(ActionEvent e)
{
    if (e.getSource() == b1)
    {
        ++turn;
        p11.remove(b1);
        if (turn % 2 == 1) { s1 = 'x'; p11.add(xLabel); }
        else if (turn % 2 == 0) { s1 = 'o'; p11.add(oLabel); }
        p11.repaint();
    }
    //Other action events inserted here
}
public static void main(String[] args) 
{
    TicTacToe game = new TicTacToe();
}
}

Picture of the problem

2个回答

3
尝试在您的JPanel实例上调用revalidate();然后再调用repaint();,像这样:

revalidate(); 然后 repaint();

        p11.revalidate();
        p11.repaint();

当添加或删除组件时,需要调用revalidate()方法,这是一条指令,告诉LayoutManager根据新的Component列表进行重置。 revalidate()会触发对组件认为是“脏区域”的repaint()方法的调用。显然,并非所有在您的JPanel上的区域都被RepaintManager视为脏区域。 repaint()用于告诉组件重新绘制自己。通常情况下,您需要调用此方法来清除类似于您的条件。

2
如果您能解释一下为什么这种方法可能是有效的,那将非常有用。 - mre

1
@Override
public void actionPerformed(final ActionEvent e)
{
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            if (e.getSource() == b1) {
                ++turn;
                p11.remove(b1);
                if (turn % 2 == 1) { s1 = 'x'; p11.add(new JLabel(iconX)); }
                else { s1 = 'o'; p11.add(new JLabel(iconO)); }
                //p11.revalidate();
                //p11.repaint();
            }
            **Other action events inserted here
        }
    });
}

invokeLater 构造有点复杂,但它让事件处理线程处理按钮点击,并在稍后进行更改。否则,您不能依赖于立即重绘,GUI 变得不太响应。(Runnable 对象只能访问外部的 final 变量,也就是说:不再分配变量。)

JLabel 这样的组件只有一个父组件字段。因此,无法重用一个组件。因此需要使用 new JLabel()

关于重绘;始终首先尝试不自己触发它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接