处理后的 JFrame 搞砸了无限 while 循环

Disposed JFrame messing up infinite while loop

所以我有一个程序,它通过扫描仪获取一些值,然后相应地更改 JFrame 上的 BufferedImage。它改变框架的方式通过一个无限的 while 循环无限期地继续下去。这工作很快(在几秒钟内清除整个 1920x1080 帧)并且在没有 SwingWorkerInvokeLater 或任何东西的情况下工作正常。

为了远离扫描仪更新程序,我决定在主程序开始之前制作另一个框架来输入变量。新框架采用这些值,单击 JButton 后,自行处理并将值发送到主程序。

问题是,一旦将这些变量传递给主程序,该程序中的主框架将自行创建但冻结,并且是完全透明的。这最初让我措手不及,因为我可以看到我的 Eclipse window 但无法单击任何内容,因为框架挡住了路,覆盖了整个屏幕。

我尝试并成功地使用了 SwingWorkerInvokeLater,但是他们中的任何一个覆盖框架所花费的时间从几秒变成了大约三十分钟(我计算过,没有实际上要等那么久)。我不确定为什么我不能使用框架而不是扫描仪来获取变量,因为无论哪种方式,变量都会传递给主程序,并且前一帧会被处理掉。我对 EDT 或 Swing Event Queue 不太熟悉,因此非常感谢您的帮助。

import java.awt.Color;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Colors {

    public static void main(String[] args) {
        /*
            Scanner scanner = new Scanner(System.in);
            System.out.println("Enter Color Choice (Red/Green/Blue)");
            String colorInput = scanner.nextLine();

            Color changingColor = new Color(0, 0, 0);
            if (colorInput.equals("Red"))
                changingColor = Color.RED;
            else if (colorInput.equals("Green"))
                changingColor = Color.GREEN;
            else if (colorInput.equals("Blue"))
                changingColor = Color.BLUE;

            scanner.close();
            changing(changingColor);
         */

        //OR

        /*
            JFrame frame = newJFrame("Start // Menu");
    
            String[] colors = {"Red", "Green", "Blue"};
            JSpinner colorSpinner = new JSpinner(new SpinnerListModel(colors));
            colorSpinner.setBounds(frame.getWidth()/2 - 40, frame.getHeight()/3 - 20, 80, 40);
            frame.add(colorSpinner);
    
            JButton okButton = new JButton("OK");
            okButton.setBounds(frame.getWidth()/2 - 40, frame.getHeight()/2 - 20, 80, 40);
            okButton.addActionListener(event -> {
                Color changingColor = new Color(0, 0, 0);
                if (colorSpinner.getValue().equals("Red"))
                    changingColor = Color.RED;
                else if (colorSpinner.getValue().equals("Green"))
                    changingColor = Color.GREEN;
                else if (colorSpinner.getValue().equals("Blue"))
                    changingColor = Color.BLUE;
    
                frame.dispose();
    
                changing(changingColor);
            });
            frame.add(okButton);
            frame.setVisible(true);
        */
    }

    public static JFrame newJFrame(String title) {
        JFrame frame = new JFrame(title);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setUndecorated(true);
        frame.setSize(1920, 1080);
        frame.setLayout(null);
        return frame;
    }

    public static void changing(Color changingColor) {
        Color color = changingColor;

        JFrame frame = newJFrame("Color Changer // Main");

        BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB);

        JLabel imageL = new JLabel();
        imageL.setBounds(frame.getBounds());
        imageL.setIcon(new ImageIcon(image));
        frame.add(imageL);

        frame.setVisible(true);

        int x = 0, y = 0;
        while (true) {
            image.setRGB(x, y, color.getRGB());

            imageL.setIcon(new ImageIcon(image));
            frame.repaint();

            x++;
            if (x >= frame.getWidth()) {
                x = 0;
                y++;
                if (y >= frame.getHeight()) {
                    y = 0;

                    if (color.equals(Color.RED)) {
                        color = Color.GREEN;
                    } else if (color.equals(Color.GREEN)) {
                        color = Color.BLUE;
                    } else if (color.equals(Color.BLUE)) {
                        color = Color.RED;
                    }
                }
            }
        }
    }
}

下面的代码是一个JFrame的mre,背景颜色反复变化。
实施的主要变化是:

  1. 删除了 edt 中的循环。用SwingTimer来控制动画
  2. 是否所有定制绘画都在 JPanel 而不是 JFrame
  3. 在不使用 BufferedImage 的情况下更改了背景颜色(可以使用 BufferedImage,但不是必需的)。
  4. 使用 JOptionPane 获取用户输入而不是 JFrame

    import java.awt.*;
    import java.util.Scanner;
    import javax.swing.*;
    
    public class Colors {
    
        public static final int CLI = 0, GUI = 1;
        private Color color = Color.RED;//default value
        private static final int CYCLE_TIME = 1000;
        private static final String[] colorsNames = {"Red", "Green", "Blue"};
    
        public Colors(int userInputBy) {
    
            if(userInputBy == CLI){
                cliInput();
            }else if (userInputBy == GUI){
                guiInput();
            }//or else use default
    
            changing();
        }
    
        private void guiInput() {
    
            //use JOptionPane to get input from user 
            JSpinner colorSpinner = new JSpinner(new SpinnerListModel(colorsNames));
            int answer = JOptionPane.showOptionDialog(null, colorSpinner, "Select color", JOptionPane.OK_CANCEL_OPTION,
                                                    JOptionPane.QUESTION_MESSAGE, null, null, null);
            String colorName ="";
            if (answer == JOptionPane.OK_OPTION) {// user selected a color
                colorName = (String) colorSpinner.getValue();
            }
    
            colorByName(colorName);
        }
    
        private void cliInput() {
    
            Scanner scanner = new Scanner(System.in);
            System.out.println("Enter Color Choice (Red/Green/Blue)");
            String colorName = scanner.nextLine();
            scanner.close();
            colorByName(colorName);
        }
    
        private void colorByName(String colorName) {
    
            if (colorName.equals(colorsNames[0])) {
                color = Color.RED;
            } else if (colorName.equals(colorsNames[1])) {
                color = Color.GREEN;
            } else if (colorName.equals(colorsNames[2])) {
                color = Color.BLUE;
            }
        }
    
        private void changing() {
    
            JFrame frame = newJFrame("Color Changer // Main");
            ChangingColorPane ccp = new ChangingColorPane(color);
            frame.add(ccp);
            frame.pack();
            frame.setVisible(true);
            //use swing time to animate
            Timer timer = new Timer(CYCLE_TIME, e->{
                changeColor();
                ccp.setBgColor(color);
                frame.repaint();
            });
            timer.start();
        }
    
        private void changeColor() {
            if (color.equals(Color.RED)) {
                color = Color.GREEN;
            } else if (color.equals(Color.GREEN)) {
                color = Color.BLUE;
            } else if (color.equals(Color.BLUE)) {
                color = Color.RED;
            }
        }
    
        //do custom painting on a JPanel
        class ChangingColorPane extends JPanel{
    
            private static final int W = 400, H = 500;
            private Color bgColor;
            public ChangingColorPane(Color bgColor) {
                this.bgColor = bgColor;
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(bgColor);
                g.fillRect(0, 0, getWidth(), getHeight());
            }
    
            @Override
            public Dimension preferredSize() {
                return new Dimension(W, H);
            }
    
            public void setBgColor(Color bgColor) {
                this.bgColor = bgColor;
            }
        }
    
        public static void main(String[] args) {
            new Colors(1);
        }
    
        private static JFrame newJFrame(String title) {
            JFrame frame = new JFrame(title);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationByPlatform(true);
            frame.setUndecorated(true);
            return frame;
        }
    }

    

在 doInBackground() 中发布无限 while 循环的作品的 SwingWorker 效果很好。出于某种原因,当我早些时候尝试过 SwingWorker 时,我 运行 它一次,然后在它完成后再次 运行 它。显然,这对 Swing 并发和 SwingWorkers 来说还是很新的。非常感谢@camickr 和@sorifiend!!