java - Swing Worker 和 GUI 更新

标签 java swing interface swingworker

我是这个社区的新人!

我想问您一些有关 SwingWorker 及其与 GUI 的关系的问题。

我知道有一些关于 SwingWorker 的问题已得到解答,而且我已经阅读了其中的很多内容,并获取了一些有用的建议。

现在我想发布一些我为基本应用程序编写的代码,该应用程序计算指定目录中的文件和文件夹的数量。

由于搜索可能需要很长时间,我希望在此过程中显示进度条。 另外,我希望用户能够通过单击按钮或简单地关闭包含进度条的框架来停止计数过程。

下面发布的代码有一些问题:

  • 对SwingWorker的execute()方法的调用是WaitingFrame构造函数的最后一条指令:有更好的地方吗?
  • WaitingFrame 的 dispose() 方法是从 SwingWorker 的 did() 方法调用的,正确吗? 如果计数过程非常快,是否可以在等待帧实际可见之前调用 dispose 方法? 结果,我会有两个开放式框架......
  • 是否有更好的方法来中断进程并管理向用户显示的消息对话框? 我使用了两个 boolean 变量,有效的和中断的,来达到我的目的......

这是代码:

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import javax.swing.*;
import javax.swing.border.*;
public class CountFiles
{
    public static void main(String[] args)throws Exception
    {
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                try
                {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    new CountFilesFrame().setVisible(true);
                }
                catch(Exception ex){
                    ex.printStackTrace();
                }
            }
        });
    }
}
class CountFilesFrame extends JFrame
{
    private JTextField field;
    public CountFilesFrame()
    {
        super("Conta File e Cartelle");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setResizable(false);
        JPanel pane=(JPanel)getContentPane();
        pane.setBackground(Color.WHITE);
        pane.setBorder(new EmptyBorder(5,20,5,20));
        JPanel center=new StyledPanel(pane,BorderLayout.CENTER,new FlowLayout(FlowLayout.LEFT,5,10)),bottom=new StyledPanel(pane,BorderLayout.SOUTH,new FlowLayout(FlowLayout.LEFT,20,0));
        // Center panel
        center.add(new JLabel("Cartella :"));
        String text="";
        try{
            File folder=new File("../");
            text=folder.exists()?folder.getCanonicalPath():"";
        }
        catch(Exception ex){}
        field=new JTextField(text,25);
        center.add(field);
        // JTextArea
        String newLine=System.getProperty("line.separator"),message="Scegliere la cartella da cui far partire la ricerca."+newLine+
        "Sara' contato il numero di file e di cartelle presenti "+newLine+"nella directory inserita e in tutte le sottocartelle";
        JTextArea area=new JTextArea(message);
        area.setEditable(false);
        area.setFont(field.getFont());
        pane.add(area,BorderLayout.NORTH);
        // Bottom panel
        bottom.add(new JButton(new AbstractAction("Cambia Cartella"){
            public void actionPerformed(ActionEvent e){
                changeDirectory();
            }
        }));
        bottom.add(new JButton(new AbstractAction("Inizia ricerca"){
            public void actionPerformed(ActionEvent e){
                new WaitingFrame(CountFilesFrame.this);
            }
        }));
        pack();
        setLocationRelativeTo(null);
    }
    public void changeDirectory()
    {
        JFileChooser chooser=new JFileChooser(field.getText());
        chooser.setDialogTitle("Cambia Cartella");
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        if(chooser.showDialog(this,"Scegli")==JFileChooser.APPROVE_OPTION)
        {
            try
            {
                File selected=chooser.getSelectedFile();
                if(selected.exists())field.setText(selected.getCanonicalPath());
            }
            catch(Exception ex){}
        }
    }
    private class WaitingFrame extends JFrame
    {
        private Counter counter;
        public WaitingFrame(CountFilesFrame f)
        {
            super("Ricerca File");
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            addWindowListener(new WindowAdapter(){
                public void windowClosing(WindowEvent e){
                    stopCounter();
                }
            });
            setResizable(false);
            JPanel pane=(JPanel)getContentPane(),buttonPanel=new StyledPanel(pane,BorderLayout.SOUTH,new FlowLayout(FlowLayout.CENTER,0,10));
            JLabel label=new JLabel("Conteggio in corso...",JLabel.CENTER);
            label.setBorder(new EmptyBorder(0,0,10,0));
            pane.add(label,BorderLayout.NORTH);
            pane.setBackground(Color.WHITE);
            pane.setBorder(new EmptyBorder(10,40,0,40));
            JProgressBar progressBar=new JProgressBar(0,100);
            progressBar.setBorderPainted(false);
            progressBar.setIndeterminate(true);
            pane.add(progressBar,BorderLayout.CENTER);
            buttonPanel.add(new JButton(new AbstractAction("Annulla"){
                public void actionPerformed(ActionEvent e){
                    stopCounter();
                }       
            }));
            while(pane.getSize().width!=pane.getPreferredSize().width)pack();
            setLocationRelativeTo(null);
            setVisible(true);
            (counter=new Counter()).execute();
        }
        public void stopCounter()
        {
            counter.interrupt();
            counter.cancel(true);
        }
        private class Counter extends SwingWorker<Void,Void>
        {
            private boolean valid=true,interrupted=false;
            private int filesNumber=0,foldersNumber=0;
            protected Void doInBackground()
            {
                File folder=new File(field.getText());
                if(!folder.exists()||!folder.isDirectory())valid=false;
                else countFiles(folder);
                return null;
            }
            protected void done()
            {
                dispose();
                if(interrupted)return;
                else if(!valid)JOptionPane.showMessageDialog(CountFilesFrame.this,"Inserire una cartella valida","Percorso specificato errato",JOptionPane.ERROR_MESSAGE);
                else JOptionPane.showMessageDialog(CountFilesFrame.this,"Sono stati trovati "+(foldersNumber-1)+" cartelle e "+filesNumber+" file","Ricerca completata",JOptionPane.INFORMATION_MESSAGE);
            }
            private void countFiles(File file)
            {
                if(file.isDirectory())
                {
                    foldersNumber++;
                    for(File nested:file.listFiles())countFiles(nested);            
                }
                else filesNumber++;
            }
            public void interrupt()
            {
                interrupted=true;
            }
        }
    }
}
class StyledPanel extends JPanel
{
    public StyledPanel(JPanel parent,String position,LayoutManager layout)
    {
        super(layout);
        setBackground(Color.WHITE);
        parent.add(this,position);
    }
}

我发布了所有应用程序代码,因此您可以尝试编译并运行它。

预先感谢您的帮助!

PS:我没有更改界面语言,对此我感到抱歉。另外,我很抱歉我的英语不好......

最佳答案

The call to execute() method for SwingWorker is the last instruction of WaitingFrame constructor: is there a better place for it?

您的调用方式没有任何问题。

The dispose() method for WaitingFrame is called from SwingWorker's done() method, is it correct? If a count process is very fast, could the dispose method be called before the waiting frame is actually visible? As a result, i would have two open frames...

这是正确的,你所描述的情况不可能发生。 SwingWorker.done()EDT 上调用通过延迟 SwingUtilities.invokeLater调用,并且您在构造 SwingWorker 之前已经调用了 JFrame.setVisible(true)(在 EDT 上)。

而不是 multiple frames ,考虑使用对话框,如果您试图阻止用户输入(例如 here ),则可能是模态对话框。

Is there a better method to interrupt the process and to manage the message dialogs shown to users? I used two boolean variables, valid and interrupted, to achieve my purpose...

考虑到您当前的代码非常接近非线程安全,当然有更好的方法来做到这一点。考虑使用 AtomicBoolean如果 EDT 和 Swing Worker 都需要访问这两个成员,则不要使用 boolean

您还应该检查某处的interrupted标志,并在其发生变化时退出文件列表循环。

您还可以将任何 swing 代码包装到 SwingWorker.doInBackground() 内任意位置的 SwingUtilities.invokeLater 调用中,以进行细粒度的 GUI 更新。这样做本质上与调用 done() 具有相同的效果,但您可以控制调用的时间和次数。检查here代码示例。您自己的代码执行此操作是为了将执行从主线程传递到 main() 方法内的 EDT(此代码模式的原因是所有 swing 代码都必须在 EDT 上执行)。

关于java - Swing Worker 和 GUI 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37747072/

相关文章:

java - 正则表达式将整个字符串放入第 1 组

java - 输入 : localhost:8080 or ip:8080 Centos7 vm 时 tomcat 启动但未显示在浏览器中

java - 将 JScrollBar/JScrollPane 添加到此 JTextPane 时遇到问题

java - 重命名文件(如果存在)

java - 无法进行静态引用..或未处理的异常类型

java - JFrame 标题在失去焦点之前不会改变

java - KeyEvent.SHIFT_DOWN_MASK 与 KeyEvent.SHIFT_MASK

java - 为什么一个已实现的接口(interface)方法被声明为公共(public)的?

string - 接口(interface)类型的 Golang String 方法

c# - 了解 OOP 中的接口(interface)