使用延迟时,JPanel不会实时更新

时间:2017-11-17 04:54:47

标签: java swing user-interface timer delay

我正在创建一个用于解决迷宫的GUI程序。我已经完成了迷宫解算算法,但是当我尝试添加延迟时会发生奇怪的事情。

代码很长,所以这里有一些在GUI延迟方面做同样的事情。

import java.awt.*;
import javax.swing.*;

public class Example extends JPanel {
    JButton[] buttons = new JButton[4];
    public Example() {
    setLayout(new GridLayout(1, 4));
    for(int i = 0; i < 4; i++)
        add(buttons[i] = new JButton());
    }

    public static class SubClass extends JPanel {
        Example example;
        JButton start;

        public SubClass() {
            setLayout(new BorderLayout());
            add(example = new Example(), BorderLayout.CENTER);
            start = new JButton("Start");
            start.addActionListener(e -> {
                for(int i = 0; i < 4; i++) {
                    //insert delays here
                    example.buttons[i].setBackground(Color.BLACK);
                }
            });
            add(start, BorderLayout.SOUTH);
        }
    }

    public static void main(String[] args){
        JFrame frame = new JFrame("Title");
        frame.setSize(500, 500);
        frame.setLocation(500, 250);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new SubClass());
        frame.setVisible(true);
    }
}

当我运行代码没有任何延迟时,它完美地运行。但是,每当我尝试添加延迟时,而不是一次更新一个按钮,整个窗口就会冻结,直到所有按钮都完成更新,然后整个事情立即更新。

我尝试使用java.util.Timerjavax.swing.TimerThread.sleep,但似乎都没有。

此外,当我没有封闭面板(SubClass)并且只是直接从主方法更改内部面板(Example)时,它确实有效,并且按钮实时更新时间就像他们应该的那样。

例如,如果我将main方法更改为

    public static void main(String[] args){
        Example ex = new Example();
        JFrame frame = new JFrame("Title");
        frame.setSize(500, 500);
        frame.setLocation(500, 250);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(ex);
        frame.setVisible(true);

        for(int i = 0; i < 4; i++) {
            //insert delays here
            ex.buttons[i].setBackground(Color.BLACK);
        }
    }

回过头来看,我应该以相反的顺序制作面板,切换子类和封闭类,但此时我已经完成了我的程序编写,除了这个之外,它在所有方面都有效,所以我不想做太多的额外工作并重写一堆东西。我也不知道这是否能解决问题。

编辑:以下是完整代码:

import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.util.*;
import javax.swing.border.LineBorder;

public class StackGUIMaze extends JPanel {
    public static final String SET_START = "setStart";
    public static final String SET_END = "setEnd";
    public static final String BUILD = "build";
    public static final String RUNNING = "running";
    private static final char OPEN = 'O'; //Open
    private static final char CLOSED = 'X'; //Closed
    private static final char TRIED = '*';
    private static final char SUCCEEDED = '+';
    private static final char FAILED = '#';
    private static final char POINTER = '^';
    private static final char END = 'E';
    private static final char START = 'S';
    private static final int DELAY = 0;
    private static final Direction NORTH = Direction.NORTH;
    private static final Direction SOUTH = Direction.SOUTH;
    private static final Direction EAST = Direction.EAST;
    private static final Direction WEST = Direction.WEST;
    private static final Direction[] DIRECTIONS = {SOUTH, EAST, NORTH, WEST};
    private final int ROWS;
    private final int COLUMNS;
    private Space pointer;
    private Space end;
    private Branch moves = new Branch(null, null);
    private Space[][] buttons;
    private String mode = "";
    private Space start;
    private Timer timer = new Timer(DELAY, e -> {
        step();
        if(pointer == end) {
            this.timer.stop();
        }
    });

    public StackGUIMaze(int r, int c) {
        ROWS = r;
        COLUMNS = c;
        setLayout(new GridLayout(ROWS, COLUMNS));
        buttons = new Space[ROWS][COLUMNS];
        for(int i = 0; i < ROWS; i++) {
            buttons[i] = new Space[COLUMNS];
            for(int j = 0; j < COLUMNS; j++) {
                buttons[i][j] = new Space(OPEN, i, j);
                buttons[i][j].setBackground(Color.WHITE);
                buttons[i][j].setContentAreaFilled(false);
                buttons[i][j].setBorder(new LineBorder(new Color(100, 100, 100)));            
                buttons[i][j].setOpaque(true);
                buttons[i][j].addActionListener(e -> {
                    if(e.getSource() instanceof Space) {
                        switch(mode) {
                            case BUILD:
                                ((Space) e.getSource()).setStatus(((Space) e.getSource()).getStatus() == OPEN ? CLOSED : OPEN); //Toggles state of space
                                break;
                            case SET_START:
                                ((Space) e.getSource()).setStatus(START);
                                break;
                            case SET_END:
                                ((Space) e.getSource()).setStatus(END);
                                break;
                        }
                    }
                });
                add(buttons[i][j]);
            }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Title");
                frame.setSize(500, 500);
                frame.setLocation(500, 250);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setContentPane(new FullPanel(12, 12));
                frame.setVisible(true);
        });
    }

    private static void delay(int n) {
        try {Thread.sleep(n);} catch(InterruptedException ignored) {}
    }

    private void setEnd(int r, int c) {
        end = null;
        buttons[r][c].setStatus(END);
    }

    public Space getPointer() {
        return pointer;
    }

    public Space getEnd() {
        return end;
    }

    private void backtrack(){
        while(openMoves().isEmpty()) {
            pointer.setStatus(FAILED);
            move(lastMove().getDirection().opposite());
            lastMove().getParent().pop();
        }
    }

    private Space to(Direction d) {
        try {
            switch (d) { //No breaks necessary, since each case ends in a return statement
                case NORTH:
                    return buttons[pointer.getRow() - 1][pointer.getCol()];
                case SOUTH:
                    return buttons[pointer.getRow() + 1][pointer.getCol()];
                case EAST:
                    return buttons[pointer.getRow()][pointer.getCol() + 1];
                case WEST:
                    return buttons[pointer.getRow()][pointer.getCol() - 1];
                default:
                    System.out.println("I don't know how you got here, somehow a nonexistent direction was called");
                    return null;
            }
        } catch(ArrayIndexOutOfBoundsException e) {
            return null;
        }
    }

    public void setPointer(int r, int c) {
        pointer = buttons[r][c];
    }

    private Branch lastMove() {
        Branch temp = moves;
        while(!temp.isEmpty()) {
            temp = temp.peek();
        }
        return temp;
    }

    private boolean go(Direction d) {
        boolean b = checkOpen(d);
        pointer.setStatus(TRIED);
        if (checkOpen(d)) {
            move(d);
            if(pointer.getStatus() == OPEN) pointer.setStatus(TRIED);
            lastMove().push(new Branch(lastMove(), d));
            return true;
        } else {
            return false;
        }
    }

    private void move(Direction d) {
        delay(50);
        switch(d) { //No breaks necessary, since each case ends in a return statement
            case NORTH:
                buttons[pointer.getRow() - 1][pointer.getCol()].setStatus(POINTER);
                break;
            case SOUTH:
                buttons[pointer.getRow() + 1][pointer.getCol()].setStatus(POINTER);
                break;
            case EAST:
                buttons[pointer.getRow()][pointer.getCol() + 1].setStatus(POINTER);
                break;
            case WEST:
                buttons[pointer.getRow()][pointer.getCol() - 1].setStatus(POINTER);
                break;
            default:
                System.out.println("I don't know how you got here, somehow a nonexistent direction was called");
        }
    }

    private ArrayList<Direction> openMoves() {
        ArrayList<Direction> arr = new ArrayList<>();
        for(Direction d : DIRECTIONS) {
            if(checkOpen(d)) arr.add(d);
        }
        return arr;
    }

    private boolean checkOpen(Direction d) {
        try {
            char s;
            switch (d) { //No breaks necessary, since each case ends in a return statement
                case NORTH:
                    s = buttons[pointer.getRow() - 1][pointer.getCol()].getStatus();
                    break;
                case SOUTH:
                    s = buttons[pointer.getRow() + 1][pointer.getCol()].getStatus();
                    break;
                case EAST:
                    s = buttons[pointer.getRow()][pointer.getCol() + 1].getStatus();
                    break;
                case WEST:
                    s = buttons[pointer.getRow()][pointer.getCol() - 1].getStatus();
                    break;
                default:
                    System.out.println("I don't know how you got here, somehow a nonexistent direction was called");
                    return false;
            }
            return s == OPEN || s == END || s == START;
        } catch(ArrayIndexOutOfBoundsException e) {
            return false;
        }
    }

    private boolean isSolved() {
        return end == pointer;
    }

    public void step() {
        while(pointer != end) {
            if(openMoves().isEmpty() && pointer != start) backtrack();
            while(!openMoves().isEmpty()) {
                for(Direction d : DIRECTIONS) {
                    if(pointer == end) return;
                    go(d);
                }
            }
        }
    }

    private enum Direction {
        NORTH, SOUTH, EAST, WEST;
        Direction opposite() {
            switch(this) {
                case NORTH:
                    return SOUTH;
                case SOUTH:
                    return NORTH;
                case EAST:
                    return WEST;
                case WEST:
                    return EAST;
                default:
                    System.out.println("How did you even get here? There are only 4 directions.");
                    return null;
            }
        }
    }

    public static class FullPanel extends JPanel {
        StackGUIMaze maze;
        JButton start;
        JPanel subPanel;

        public FullPanel(int r, int c) {
            maze = new StackGUIMaze(r, c);
            setLayout(new BorderLayout());
            add(maze, BorderLayout.CENTER);

            JButton start = new JButton();
            start.setText("Start");
            start.addActionListener(e -> {
                if(maze.getPointer() == null || maze.getEnd() == null) {
                    System.out.println("Could not solve, as the start/end is missing.");
                } else {
                    for (Space[] sp : maze.buttons) {
                        for (Space s : sp) {
                            s.setEnabled(false);
                        }
                    }
                    maze.timer.start();
                }
            });

            JButton setEnd = new JButton("Set end");
            setEnd.addActionListener(e -> maze.mode = SET_END);

            JButton setStart = new JButton("Set start");
            setStart.addActionListener(e -> maze.mode = SET_START);

            JButton buildButton = new JButton("Build maze");
            buildButton.addActionListener(e -> maze.mode = BUILD);

            subPanel = new JPanel();
            subPanel.add(setStart);
            subPanel.add(setEnd);
            subPanel.add(buildButton);
            subPanel.add(start);
            add(subPanel, BorderLayout.SOUTH);
        }
    }

    private class Branch extends Stack<Branch> {
        Branch parent;
        private Direction direction;

        public Branch(Branch b, Direction d) {
            parent = b;
            direction = d;
        }

        public Direction getDirection() {
            return direction;
        }

        public Branch getParent() {
            return parent;
        }
    }

    private class Space extends JButton {
        int row;
        int col;
        private char status;

        public Space(char status, int row, int col) {
            this.status = status;
            this.row = row;
            this.col = col;
        }

        public char getStatus() {
            return status;
        }

        public void setStatus(char s) {
            char st = status;
            this.status = s;
            switch(s) {
                case OPEN:
                    setBackground(Color.WHITE);
                    break;
                case CLOSED:
                    setBackground(Color.BLACK);
                    break;
                case TRIED:
                    if(st == FAILED) status = FAILED;
                    else setBackground(Color.LIGHT_GRAY);
                    break;
                case FAILED:
                    setBackground(Color.RED);
                    break;
                case SUCCEEDED:
                    setBackground(Color.GREEN);
                    break;
                case POINTER:
                    if(pointer != null && !mode.equals(RUNNING)) pointer.setStatus(TRIED);
                    else if(pointer != null) pointer.setStatus(OPEN);
                    setBackground(Color.GRAY);
                    pointer = this;
                    break;
                case END:
                    if(end != null) end.setStatus(OPEN);
                    setBackground(Color.CYAN);
                    end = this;
                    break;
                case START:
                    if(start != null) pointer.setStatus(START);
                    setBackground(Color.MAGENTA);
                    start = this;
                    pointer = start;
                    break;
                default:
                    System.out.println("Invalid status passed to method setStatus()");
            }
        }

        public int getRow() {
            return row;
        }

        public void setRow(int row) {
            this.row = row;
        }

        public int getCol() {
            return col;
        }

        public void setCol(int col) {
            this.col = col;
        }
    }
}

我意识到它可能不是最佳的,但我只是想在开始尝试改进它之前让它工作。

1 个答案:

答案 0 :(得分:2)

所以,你有两个基本问题:

  1. 您正在阻止ActionListener内的事件调度线程。在actionPerformed方法返回
  2. 之后,才会更新UI
  3. 设置JButton的背景比其他组件稍微复杂一些。首先,我停用了contentAreaFilledborderPainted属性,并设置了按钮opaque,使其可以填充。
  4. 例如......

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Example extends JPanel {
    
        JButton[] buttons = new JButton[4];
    
        public Example() {
            setLayout(new GridLayout(1, 4));
            for (int i = 0; i < 4; i++) {
                add(buttons[i] = new JButton());
                buttons[i].setContentAreaFilled(false);
                buttons[i].setBorderPainted(false);
                buttons[i].setOpaque(true);
            }
        }
    
        public static class SubClass extends JPanel {
    
            Example example;
            JButton start;
    
            private int counter = 0;
    
            public SubClass() {
                setLayout(new BorderLayout());
                add(example = new Example(), BorderLayout.CENTER);
                start = new JButton("Start");
                start.addActionListener(e -> {
                    counter = 0;
                    Timer timer = new Timer(200, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (counter >= 4) {
                                ((Timer) e.getSource()).stop();
                                return;
                            }
                            example.buttons[counter].setBackground(Color.BLACK);
                            counter++;
                        }
                    });
                    timer.start();
                });
                add(start, BorderLayout.SOUTH);
            }
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Title");
                    frame.setSize(500, 500);
                    frame.setLocation(500, 250);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setContentPane(new SubClass());
                    frame.setVisible(true);
                }
            });
        }
    }