创建一个 'loading...' 动画帧以显示为正在执行的方法?

Creating a 'loading...' animation frame to display as a method is being executed?

我有一个 FileFinder.java 和一个带有 GUI 的 Loading.java class。 FileFinder.java 允许用户 select 目录并在该目录中搜索文件。现在,对于更大的目录,搜索需要一些时间才能完成,我不想让用户怀疑它是否真的在搜索,所以我试图显示另一个框架 (Loading.java)。

这是我在单击“搜索”按钮时的代码:

 private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:

        String dirName = "";
        String username = System.getProperty("user.name");

        if(cdriveButton.isSelected()){
            dirName = "C:/Users/" + username;
        }
        else if(pdriveButton.isSelected()){
            dirName = "P:";
        }
        else if(xdriveButton.isSelected()){
            dirName = "X:";
        }
        else if(customButton.isSelected()){
            dirName = JOptionPane.showInputDialog(rootPane, "Enter the directory you would like to search in: ", "Enter Directory", HEIGHT);           
        }   

          String search = filenameText.getText();
          File root = new File(dirName);
          resultText.setText("");


        Loading show = new Loading();

        show.setVisible(true);

        displayDirectoryContents(root, search);

        show.setVisible(false);



    }                 

这里是 Loading.java:

public class Loading extends javax.swing.JFrame {

    /**
     * Creates new form Loading
     */
    public Loading() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jLabel1.setText("Please wait...");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(148, 148, 148)
                .addComponent(jLabel1)
                .addContainerGap(186, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(122, 122, 122)
                .addComponent(jLabel1)
                .addContainerGap(164, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Loading.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Loading.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Loading.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Loading.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Loading().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JLabel jLabel1;
    // End of variables declaration                   
}

一切正常,除了一个小问题:它没有在加载框架内正确显示 GUI。当我按下按钮时它会弹出,并一直保持到搜索完成,然后正确关闭,但它应该显示一个标签,上面写着 "Please wait..."。它没有显示该标签,它只显示一个空白的白色框。

Edit1:找到以下解决方案。转换为 JDialog 窗格而不是 JFrame 并添加一个 SwingWorker:

        JDialog jDialog = new JDialog();
        jDialog.setLayout(new GridBagLayout());
        jDialog.add(new JLabel("Please wait..."));
        jDialog.setMinimumSize(new Dimension(150, 50));
        jDialog.setResizable(false);
        jDialog.setModal(false);
        jDialog.setUndecorated(true);
        jDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        jDialog.setLocationRelativeTo(null);
        jDialog.setVisible(true);

       SwingWorker swingWorker = new SwingWorker<Void, Void>() {
    @Override
    protected Void doInBackground() throws Exception {
        displayDirectoryContents(root, search);
        return null;
    }
};

你的问题在这里:

displayDirectoryContents(root, search);

您 运行 在 Java 事件线程或 EDT(事件调度线程)上执行此操作。当 运行 花费的时间超过片刻时,它会阻止 EDT 并阻止它做它需要做的事情,包括显示你的加载 window(顺便说一句应该是一个 对话框 window 例如 JDialog 而 不是 应用程序 window 或 JFrame)。

解决方案:运行 此方法在后台线程中调用,例如 SwingWorker 提供的线程。显示您的 对话框 window,并在工作人员完成时使用回调通知您,因此您不应再显示加载对话框 window。

例如,这个:

String search = filenameText.getText();
File root = new File(dirName);
resultText.setText("");

Loading show = new Loading();
show.setVisible(true);
displayDirectoryContents(root, search);
show.setVisible(false);

可以更改为类似(注意代码未测试)

String search = filenameText.getText();
File root = new File(dirName);
resultText.setText("");

Loading show = new Loading();
show.setVisible(true);

// create our worker
new SwingWorker<Void, Void> worker = new SwingWorker<>(){
    @Override
    protected Void doInBackground() throws Exception {
        displayDirectoryContents(root, search);
        return null;
    }
};

worker.addPropertyChangeListener(evt -> {
    if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
        // you should also call get() on the worker allowing
        // you to capture and handle all exceptions it might throw
        show.setVisible(false);
    }
});

worker.execute();  // run the worker

更多请查看:Lesson: Concurrency in Swing

你的问题的解决方法参考鳗鱼的@Hovercraft 回答

附带回答一下,使用多个 JFrames 是不好的做法,我建议您使用 JDialog。看这个例子:

JDialog jDialog = new JDialog();
jDialog.setLayout(new GridBagLayout());
jDialog.add(new JLabel("Please wait..."));
jDialog.setMinimumSize(new Dimension(150, 50));
jDialog.setResizable(false);
jDialog.setModal(false);
jDialog.setUndecorated(true);
jDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
jDialog.setLocationRelativeTo(null);
jDialog.setVisible(true);

这将在屏幕中央的灰色小框中显示消息 "Please wait...",没有关闭按钮或标题栏。

由于 setModal(false) 选项,它也是 non-blocking。因此其他代码可以在显示时执行。