Swing Worker 和 GUI 更新
Swing Worker and GUI update
我是这个社区的新手!
我想问你一些关于 SwingWorker 及其与 GUI 的关系。
我知道有一些关于 SwingWorker 的问题已得到解答,我已经阅读了很多,并采纳了一些有用的建议。
现在我想 post 我为一个基本应用程序编写的一些代码,该应用程序计算指定目录中的文件和文件夹的数量。
由于搜索可能会花费很多时间,我希望在此过程中显示一个进度条。
另外,我希望用户可以通过单击按钮或简单地关闭包含进度条的框架来停止计数过程。
这里有一些关于代码 post 的问题:
- 调用SwingWorker的execute()方法是WaitingFrame构造函数的最后一条指令:有没有更好的地方?
- WaitingFrame的dispose()方法是从SwingWorker的done()方法中调用的,对吗?
如果计数过程非常快,是否可以在等待帧实际可见之前调用 dispose 方法?
结果,我将有两个打开的框架...
- 有没有更好的方法来中断进程和管理显示给用户的消息对话框?
我使用了两个布尔变量,valid 和 interrupted,来达到我的目的...
这是代码:
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);
}
}
我post编辑了所有应用程序代码,因此您可以尝试编译并运行它。
在此先感谢您的帮助!
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 via a delayed SwingUtilities.invokeLater
调用中调用,您在构建 SwingWorker
之前已经调用了 JFrame.setVisible(true)
(在美国东部时间)。
而不是 multiple frames, consider using a dialog, perhaps a modal one if you are attempting to block user input (like )。
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...
考虑到您当前的代码接近非线程安全的危险,肯定有更好的方法来执行此操作。如果 EDT 和 swing worker 都需要访问这两个成员,请考虑使用 AtomicBoolean
而不是 boolean
。
您还应该检查某处的 interrupted
标志,并在它发生变化时退出您的文件列表循环。
您还可以将任何 swing 代码包装到 SwingUtilities.invokeLater
调用 SwingWorker.doInBackground()
内的任何位置,以进行细粒度的 GUI 更新。这样做本质上与调用 done()
具有相同的效果,但您可以控制调用的时间和次数。检查 以获取代码示例。您自己的代码这样做是为了将执行从主线程传递到您的 main()
方法中的 EDT(此代码模式的原因是所有 swing 代码都必须在 EDT 上执行)。
我是这个社区的新手!
我想问你一些关于 SwingWorker 及其与 GUI 的关系。
我知道有一些关于 SwingWorker 的问题已得到解答,我已经阅读了很多,并采纳了一些有用的建议。
现在我想 post 我为一个基本应用程序编写的一些代码,该应用程序计算指定目录中的文件和文件夹的数量。
由于搜索可能会花费很多时间,我希望在此过程中显示一个进度条。 另外,我希望用户可以通过单击按钮或简单地关闭包含进度条的框架来停止计数过程。
这里有一些关于代码 post 的问题:
- 调用SwingWorker的execute()方法是WaitingFrame构造函数的最后一条指令:有没有更好的地方?
- WaitingFrame的dispose()方法是从SwingWorker的done()方法中调用的,对吗? 如果计数过程非常快,是否可以在等待帧实际可见之前调用 dispose 方法? 结果,我将有两个打开的框架...
- 有没有更好的方法来中断进程和管理显示给用户的消息对话框? 我使用了两个布尔变量,valid 和 interrupted,来达到我的目的...
这是代码:
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);
}
}
我post编辑了所有应用程序代码,因此您可以尝试编译并运行它。
在此先感谢您的帮助!
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 via a delayed SwingUtilities.invokeLater
调用中调用,您在构建 SwingWorker
之前已经调用了 JFrame.setVisible(true)
(在美国东部时间)。
而不是 multiple frames, consider using a dialog, perhaps a modal one if you are attempting to block user input (like
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...
考虑到您当前的代码接近非线程安全的危险,肯定有更好的方法来执行此操作。如果 EDT 和 swing worker 都需要访问这两个成员,请考虑使用 AtomicBoolean
而不是 boolean
。
您还应该检查某处的 interrupted
标志,并在它发生变化时退出您的文件列表循环。
您还可以将任何 swing 代码包装到 SwingUtilities.invokeLater
调用 SwingWorker.doInBackground()
内的任何位置,以进行细粒度的 GUI 更新。这样做本质上与调用 done()
具有相同的效果,但您可以控制调用的时间和次数。检查 main()
方法中的 EDT(此代码模式的原因是所有 swing 代码都必须在 EDT 上执行)。