Java Swing 2D 面板数组不使用 Swing 计时器重新绘制

Posted

技术标签:

【中文标题】Java Swing 2D 面板数组不使用 Swing 计时器重新绘制【英文标题】:Java Swing 2D Array of Panels not repainting with Swing Timers 【发布时间】:2016-03-19 09:08:51 【问题描述】:

我是 Java Swing 的新手,我一直致力于创建一个 Connect 4 游戏,该游戏通过带有 gameRoom 的服务器支持多人游戏。我已经解决这个特殊问题将近 2 天,在解决它的同时解决了一些问题,例如停止使用 Thread.sleep 并彻底测试了游戏对象是否正确地传入和传出服务器。

我的架构方式是通过模型的 make move 命令提交每个动作。为了刷新 GUI,服务器将一个新的 Game 对象发送回模型并将控制器属性“setRepaint”标记为 true。然后,计时器会定期检查该属性是否为真并调用 repaintGrid() 方法。

经过数小时的尝试,我无法重新绘制游戏面板。

可能有帮助的几点:

如果我退出应用程序并使用已在其上移动的 Game 对象重新启动它,则会绘制面板。问题在于重绘方法。

模型是静态的,每次调用 Connect4App.model.getGameFromServer() 时都会更新它的游戏属性。不确定这是否会导致问题,但如果我打印正在重新绘制为红色/蓝色的面板,我可以验证服务器在每次迭代中成功更新了游戏对象。

框架层次结构如下: guiMain 是 gamePanel 的容器,它有一个网格布局,每个都由一个 GridPanel 面板填充。 Grid 面板本质上是 Connect4 游戏令牌的插槽,这些是我尝试在 repaintGrid 方法中更新的插槽

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.Timer;

public class GameController 

MouseAdapter me;
private JPanel gamePanel;
private boolean setRepaint = false;

/**
 * Constructor for the Game Controller. Takes in a view and a model
 * 
 * @param view
 * @param model
 */
public GameController() 
    setupGridPanels();
    setupMouseAdapter();
    Connect4App.frame.setContentPane(Connect4App.guiMain);
    Connect4App.frame.setTitle("Game View");
    goIntoTimer();



/**
 * Repaint Boolean used by timer. Set to true by external program
 */
public void setRepaint() 
    this.setRepaint = true;


/**
 * Swing Timer which checks if it needs to repaint every 8 seconds and if
 * so, calls repaintGrid
 */
private void goIntoTimer() 
    Timer timer = new Timer(50, new ActionListener() 
        @Override
        public void actionPerformed(ActionEvent e) 
            Connect4App.model.getGameFromServer();

            if (setRepaint == true) 
                repaintGrid();
                setRepaint = false;
            

        
    );
    timer.setRepeats(true);
    timer.setDelay(8000);
    timer.start();



/**
 * Sets up the Initial Game Panels in the Connect4App.guiMain panel
 */
private void setupGridPanels() 
    this.gamePanel = new JPanel();
    this.gamePanel.removeAll();

    // setting up the layout for, the game board.
    this.gamePanel.setLayout(new GridLayout(0, Connect4App.model.getGame().getGrid()[0].length));

    int numberOfRows = Connect4App.model.getGame().getGrid().length;
    int numberOfColumns = Connect4App.model.getGame().getGrid()[0].length;

    for (int r = 0; r < numberOfRows; r++) 
        for (int c = 0; c < numberOfColumns; c++) 
            Connect4App.guiMain.setCircleArc(r, c, new GridPanel(r, c));
            this.gamePanel.add(Connect4App.guiMain.getCircleArcs()[r][c]);
        
    
    Connect4App.guiMain.add(this.gamePanel);


/**
 * Sets up the mouse pressed event handleres for every panel
 */
private void setupMouseAdapter() 
    MouseAdapter mc = new MouseAdapter() 

        @Override
        public void mousePressed(MouseEvent mc) 

            GridPanel cell = (GridPanel) mc.getSource();

            // this is the column that should go in the MakeMove message
            int column = cell.getColumn();
            int row = cell.getRow();

            if (Connect4App.model.getGame().getGrid()[row][column].getState() == 0) 
                System.out.println("attempting to make move");
                Connect4App.model.makeMove(column);
            
        
    ;

    for (int r = 0; r < Connect4App.model.getGame().getGrid().length; r++) 
        for (int c = 0; c < Connect4App.model.getGame().getGrid()[0].length; c++) 
            Connect4App.guiMain.getCircleArcs()[r][c].addMouseListener(mc);
        
    


void repaintGrid() 

    // --> This is supposed to be working
    System.out.println("repainting");

    for (int r = 0; r < Connect4App.model.getGame().getGrid().length; r++) 
        for (int c = 0; c < Connect4App.model.getGame().getGrid()[0].length; c++) 
            Connect4App.guiMain.getCircleArcs()[r][c].validate();
            Connect4App.guiMain.getCircleArcs()[r][c].repaint();
        
    


任何帮助将不胜感激:-D

【问题讨论】:

似乎已经完成了对静态的使用,这可能是一个问题。读取/写入状态变量的线程之间也可能存在问题 感谢您的回答@MadProgrammer。我进行了测试,似乎在重绘调用paintComponent方法时状态正在正确更新:-s 你能在循环中打印出调试信息吗? (另外,我可能是错的,但你确定validate() 是正确的吗?revalidate()invalidate() 怎么样?我已经有一段时间没有做过这样的事情了。) 此代码未显示 GridPanel 内容的更改位置(从 getCircleArcs() 返回的内容)。你能把那部分也发一下吗? 作为旁注,如果您发布SSCCE,对您的帮助会容易得多。 【参考方案1】:

事实证明,我在通过 objectoutputstreams(游戏状态)发送回和第四个对象时遇到问题,而没有重置流,因此这导致客户端始终无法接收更新。上面的重绘代码正在工作。关闭线程,再次感谢。

【讨论】:

以上是关于Java Swing 2D 面板数组不使用 Swing 计时器重新绘制的主要内容,如果未能解决你的问题,请参考以下文章

java swing问题:JFrame根面板不透明且可见,内容面板不透明且可见,层面板透明且可见,

JAVA学习Swing章节标签JLabel中图标的使用

(转载) Java Swing 之 JScrollPane (滚动面板) 使用方法

如何使用 Java AWT/Swing 垂直对齐面板

使用Swing和Graphics2D在Java中旋转滚轮?

Java Swing:在 JTabbedPane 的未选择面板上更改隐藏的 UI 元素时发出声音