为什么该线程代码无法与GUI一起正常工作? [Java Swing] [线程]

时间:2018-11-10 18:24:46

标签: java swing awt border-layout towers-of-hanoi

我的项目使用 Java Swing 作为GUI。我正在制作河内塔游戏。我几乎可以正常使用GUI,但是我的Solve命令无法正常工作。

在没有线程调用的情况下,它可以按预期立即解决塔楼问题。我添加了几个Thread.waits期望它可以逐步解决它,以便用户可以看到它是如何工作的,但是相反,它要等待一段时间,然后立即解决整个难题。我在想它可能不会重新粉刷,但是我不确定为什么。有人知道发生了什么吗?

这里有解决代码:

public class Solver {

public Solver() {
    // nothing
}


public void solve(
    int numberBlocks,
    int startPin,
    int auxiliaryPin,
    int endPin) {
    if (numberBlocks == 1) {
        movePin(startPin, endPin);
        try {
           Thread.sleep(200);
       }
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    else {
        solve(numberBlocks - 1, startPin, endPin, auxiliaryPin);
        movePin(startPin, endPin);
        try {
            Thread.sleep(200);
        }
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        solve(numberBlocks - 1, auxiliaryPin, startPin, endPin);
    }
}


private void movePin(int startPin, int endPin) {
    TowersOfHanoiGame.moveTopBlock(startPin, endPin);
}

这是完成工作的GUI中的代码: 我知道它写得很糟糕,这是我第一次使用Java Swing编写,我正在学习中。如果有人对如何更好地构建结构有任何建议,我也很想听听。 我要粘贴整个类,但是重要的方法是initListeners和moveTopBlock及其调用的方法。

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class TowersOfHanoiGame {
private static JFrame mainWindow;
private static JPanel mainContentPanel;
private static JPanel content;

private static ArrayList<Block> pegOneBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegTwoBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegThreeBlocks = new ArrayList<Block>();
private Color[] randomColors = new Color[8];

private Dimension menuSize = new Dimension(100, 100);
private static final int DISCSTEXTSIZE = 20;
private static final int MOVESTEXTSIZE = 30;

private ActionListener downButtonListener;
private ActionListener upButtonListener;
private ActionListener solveButtonListener;

private JLabel discs;
private JLabel moves;

private int discsNumber = 3;
private int movesNumber = 0;

private Solver solver = new Solver();


public TowersOfHanoiGame() {
    // do nothing
    initRandomColors();
    initBlocks(3);
}


/**
 * Initialize and display the game
 */
public void display() {
    initListeners();
    initWindow();
    mainWindow.setVisible(true);
}


private void initListeners() {
    downButtonListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            if (discsNumber > 3) {
                discsNumber--;
                updateLabels();
                // clearContentFrame();
                clearBlockArrays();
                initBlocks(discsNumber);
                reDrawContentFrame();
            }

        }
    };
    upButtonListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            if (discsNumber < 8) {
                discsNumber++;
                updateLabels();
                // clearContentFrame();
                clearBlockArrays();
                initBlocks(discsNumber);
                reDrawContentFrame();
            }
        }
    };

    solveButtonListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            solver.solve(discsNumber, 0, 1, 2);
        }
    };
}


private void updateLabels() {
    discs.setText("DISCS: " + discsNumber);
    moves.setText("MOVES: " + movesNumber);
}


/**
 * Init the main window
 */
private void initWindow() {
    mainWindow = new JFrame("Towers Of Hanoi");
    initContentPanel();
    mainWindow.setContentPane(mainContentPanel);
    mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mainWindow.setSize(1000, 1000);
    mainWindow.setResizable(false);
    mainWindow.getContentPane().setBackground(Color.WHITE);
}


/**
 * Init the main content panel
 */
private void initContentPanel() {
    mainContentPanel = new JPanel(new BorderLayout(50, 50));
    JPanel menu = initMenuFrame();
    content = initContentFrame();
    mainContentPanel.add(menu, BorderLayout.PAGE_START);
    mainContentPanel.add(content, BorderLayout.CENTER);
}


private static JPanel initContentFrame() {
    JPanel ret = new JPanel(new BorderLayout());
    JPanel pegs = new JPanel(new BorderLayout());
    pegs.setBackground(Color.WHITE);
    ret.setBackground(Color.WHITE);

    Peg peg1 = new Peg(25, 500, 1.2);
    Peg peg2 = new Peg(50, 500, 1.2);
    Peg peg3 = new Peg(0, 500, 1.2);

    peg1.addBlocks(pegOneBlocks);
    peg2.addBlocks(pegTwoBlocks);
    peg3.addBlocks(pegThreeBlocks);

    pegs.add(peg1, BorderLayout.LINE_START);
    pegs.add(peg2, BorderLayout.CENTER);
    pegs.add(peg3, BorderLayout.LINE_END);

    ret.add(pegs, BorderLayout.CENTER);
    return ret;
}


private Color randomColor() {
    int R = (int)(Math.random() * 256);
    int G = (int)(Math.random() * 256);
    int B = (int)(Math.random() * 256);
    Color color = new Color(R, G, B); // random color, but can be bright or
                                      // dull

    // to get rainbow, pastel colors
    Random random = new Random();
    final float hue = random.nextFloat();
    final float saturation = 0.9f;// 1.0 for brilliant, 0.0 for dull
    final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black
    color = Color.getHSBColor(hue, saturation, luminance);
    return color;
}


private void initRandomColors() {
    for (int i = 0; i < 8; i++) {
        randomColors[i] = randomColor();
    }
}


private void initBlocks(int numBlocks) {
    int startWidth = Block.LONGESTWIDTH;
    for (int i = 0; i < numBlocks; i++) {
        Block b = new Block((startWidth - (i * 15)), randomColors[i]);
        pegOneBlocks.add(b);
    }
}


private static void clearContentFrame() {
    mainContentPanel.remove(content);
    mainContentPanel.repaint();
}


private void clearBlockArrays() {
    pegOneBlocks.clear();
    pegTwoBlocks.clear();
    pegThreeBlocks.clear();
}


public static void reDrawContentFrame() {
    content = initContentFrame();
    mainContentPanel.add(content, BorderLayout.CENTER);
    mainContentPanel.repaint();
}


public static void moveTopBlock(int startPin, int destinationPin) {
    Block b = null;
    if (startPin == 0) {
        b = pegOneBlocks.get(pegOneBlocks.size() - 1);
        pegOneBlocks.remove(pegOneBlocks.size() - 1);
    }
    else if (startPin == 1) {
        b = pegTwoBlocks.get(pegTwoBlocks.size() - 1);
        pegTwoBlocks.remove(pegTwoBlocks.size() - 1);
    }
    else if (startPin == 2) {
        b = pegThreeBlocks.get(pegThreeBlocks.size() - 1);
        pegThreeBlocks.remove(pegThreeBlocks.size() - 1);
    }

    if (destinationPin == 0) {
        pegOneBlocks.add(b);
    }
    else if (destinationPin == 1) {
        pegTwoBlocks.add(b);
    }
    else if (destinationPin == 2) {
        pegThreeBlocks.add(b);
    }

    reDrawContentFrame();
    content.validate();
    mainContentPanel.validate();
    mainWindow.validate();
}


/**
 * Build the menu panel
 * 
 * @return menu panel
 */
private JPanel initMenuFrame() {
    JPanel ret = new JPanel(new BorderLayout());
    ret.setPreferredSize(menuSize);

    // left
    JPanel left = new JPanel(new FlowLayout());
    left.setPreferredSize(menuSize);
    JLabel label = new JLabel("DISCS: 3");
    discs = label;
    label.setFont(new Font("Serif", Font.BOLD, DISCSTEXTSIZE));
    Button down = new Button("Decrease");
    down.addActionListener(downButtonListener);
    Button up = new Button("Increase");
    up.addActionListener(upButtonListener);
    left.add(label);
    left.add(up);
    left.add(down);

    // mid
    moves = new JLabel("MOVES: 0");
    moves.setHorizontalAlignment(JLabel.CENTER);
    moves.setFont(new Font("Serif", Font.BOLD, MOVESTEXTSIZE));

    // right
    JPanel right = new JPanel(new FlowLayout());
    Button solve = new Button("Solve");
    solve.addActionListener(solveButtonListener);
    Button reset = new Button("Reset");
    right.add(solve);
    right.add(reset);

    // sync
    JPanel menu = new JPanel(new BorderLayout());
    menu.add(left, BorderLayout.LINE_START);
    menu.add(moves, BorderLayout.CENTER);
    menu.add(right, BorderLayout.LINE_END);

    ret.add(menu, BorderLayout.CENTER);
    return ret;
}
}

1 个答案:

答案 0 :(得分:1)

solveButtonListener = new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
        solver.solve(discsNumber, 0, 1, 2);
    }
};

问题在于,为任何侦听器调用的代码都在Event Dispatch Thread (EDT)上执行。 EDT负责响应事件并重新绘制GUI。 Thread.sleep()方法使EDT进入睡眠状态,因此,在所有代码执行完毕之前,GUI无法重绘自身。

您需要做的是在调用Thread方法时启动一个单独的solver.solve(...)

阅读Concurrency的Swing教程中的部分,以获得更多信息。

请注意,以上建议使用单独的线程仍然不是正确的解决方案。 Swing被设计为单线程,这意味着对GUI状态的所有更新和GUI的重新绘制都应在EDT上完成。因此,这意味着您还应该使用SwingUtilities.invokeLater()将代码添加到EDT中进行处理。使用递归时,我从未尝试过这样做,所以我不确定做到这一点的最佳方法。