我有一个自定义的 JFileChooser
它的approveSelection()方法略有修改:
public void approveSelection()
{
File file = getSelectedFile();
changeGui();
final Object a = makeALongCalcualtion(file);
if (a != null)
{
super.approveSelection();
SwingWorker<Document, Void> open = new SwingWorker<Document, Void>()
{
@Override
protected Document doInBackground() throws Exception
{
return createADocument(a);
}
@Override
protected void done()
{
try
{
if(get() != null)
{
changeGui();
}
else
{
//TODO error message
changeGui();
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error message
changeGui();
}
}
};
open.execute();
}
else
{
//TODO error message
changeGui();
}
}
changeGui() 方法将 JProgressBar 设置为不确定,并使用新字符串更新 JLabel。
如果提供给 makeALongCalcualtion(file) 的文件类型无效,它将返回 null,否则它将返回传递给 SwingWorker 的信息,SwingWorker 可以使用它在程序中实际创建文件的表示(Document 对象) .
但是,这并不能正常工作,因为在 SwingWorker 方法中未调用 makeALongCalcualtion(file),这会阻止 EDT。
为了解决这个问题,我必须在 SwingWorker 中调用 makeALongCalcualtion(file)。我可以毫无问题地将这部分代码移至 SwingWorker 中,但随后我必须(由于我的代码逻辑)将 super.approveSelection() 与其一起移动。
所以底线是,对于这种特定情况,如何从 doInBackground() 中调用 super.approveSelection() ?
//更多信息
应该发生什么:
- 用户选择并打开文件
- JLabel 和 JProgressBar 已更新,开始播放不确定进度。
- 如果 makeALongCalcualtion(file) 返回 null,用户会收到错误窗口警告,但 JFileChooser stys 打开,因此可以在错误窗口关闭时再次选择。
- 否则,将调用 super.approveSelection(),从而允许关闭选择器。
- 已创建文档(但如果出现问题,创建文档的方法将返回 null)。
- 如果一切正常,JLabel 会更新并且进度条动画会停止(不确定设置为 false)。
- 如果出现问题,会发生与步骤 6 相同的情况,只是 JLabel 中的消息不同。
发生了什么:
- 相同
- 相同
- 相同,但是当 makeALongCalculation(file); 时开始,进度条卡住。
- 相同
- 相同
- 相同,但动画不会停止(因为进度条被卡住),只有卡住的“图片”被删除,进度条返回到之前的状态。
- 相同
编辑
我对我的程序做了一些修改,现在有了:
批准选择():
public void approveSelection()
{
File file = getSelectedFile();
Main.getStatusBar().startOpen();
final WorkOpen open = new WorkOpen(file);
open.execute();
open.addPropertyChangeListener(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue().equals("DONE"))
{
if (open.failed())
{
//TODO error message
Main.getStatusBar().endOpen(false);
}
else
{
Main.getStatusBar().endOpen(true);
}
}
}
}
});
}
SwingWorker:
class WorkOpen extends SwingWorker<Document, Void>
{
boolean failed = false;
File file;
public boolean failed()
{
return failed;
}
@Override
protected Document doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//My little path/name/similar managing system
FileComplex fullPath = new FileComplex(file.toString());
return Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
}
else
{
failed = true;
return null;
}
}
@Override
protected void done()
{
try
{
if(get() != null)
{
Main.addDocument(get());
}
}
catch (InterruptedException | ExecutionException e)
{
failed = true;
}
}
WorkOpen(File file)
{
this.file = file;
}
}
现在的问题是在哪里调用 super.approveSelection()。它必须等待工作线程完成执行,但我无法从属性更改监听器中调用它。
在这里做什么?
编辑2
正如 HovercraftFullOfEels 所建议的,我修复了代码并编译并运行。但JProgressBar卡住的问题依然存在。另外,我必须介绍一些我不知道是否应该介绍的内容:
private void superApproveSelection()
{
super.approveSelection();
}
public void approveSelection()
{
final File file = getSelectedFile();
class OpenWorker extends SwingWorker<Boolean, Void>
{
Document document;
Document getDocument()
{
return document;
}
@Override
protected Boolean doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//I had to start the progressBar here, because if invalid
//file was selected (extractData(file) returns null if it was),
//nothing should happen (maybe an error
//window later, handled with a new Runnable() same as this:
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
Main.getStatusBar().startOpen();
}
});
FileComplex fullPath = new FileComplex(file.toString());
document = Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
return true;
}
else
{
return false;
}
}
};
final OpenWorker opener = new OpenWorker();
opener.addPropertyChangeListener(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
if ("state".equals(evt.getPropertyName()))
{
if (evt.getNewValue() == SwingWorker.StateValue.DONE)
{
if(opener.getDocument() != null)
{
superApproveSelection();
Main.addDocument(opener.getDocument());
Main.getStatusBar().endOpen(true);
}
else
{
try
{
//I'm retrieveing doInBackground()'s value to see if
//progressBar needs stoping (it also displays some
//text, so it must not be called unless the
//progressBar was started).
if (opener.get())
{
Main.getStatusBar().endOpen(false);
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error something went wrong
}
}
}
}
}
});
opener.execute();
}
最佳答案
"In order to fix the problem, I would have to call makeALongCalcualtion(file) within a SwingWorker. I could move that part of the code into the SwingWorker without any problems, but then I would have to (due to my code logic) move super.approveSelection() along with it."
不,根本不是这样。 super.approveSelection()
不必在 SwingWorker 内部调用。
为什么不简单地创建一个 SwingWorker,向其中添加一个 PropertyChangeListener,然后在 SwingWorker 的状态完成时,根据指示调用 super.approveSelection()
?
好的,下面是我的示例。解释如下:
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.text.*;
@SuppressWarnings("serial")
public class ApproveSelectionTest extends JPanel {
private JTextArea textArea = new JTextArea(30, 60);
public ApproveSelectionTest() {
textArea.setEditable(false);
textArea.setFocusable(false);
JPanel btnPanel = new JPanel();
btnPanel.add(new JButton(new MyGetFileAction("Get Text File Text")));
setLayout(new BorderLayout());
add(new JScrollPane(textArea), BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private class MyGetFileAction extends AbstractAction {
public MyGetFileAction(String text) {
super(text);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
MyFileChooser myFileChooser = new MyFileChooser();
int result = myFileChooser.showOpenDialog(ApproveSelectionTest.this);
if (result == JFileChooser.APPROVE_OPTION) {
Document doc = myFileChooser.getDocument();
textArea.setDocument(doc);
}
};
}
private static void createAndShowGui() {
ApproveSelectionTest mainPanel = new ApproveSelectionTest();
JFrame frame = new JFrame("ApproveSelectionTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
@SuppressWarnings("serial")
class MyFileChooser extends JFileChooser {
private WorkOpen workOpen = null;
private JDialog progressDialog = null;
public MyFileChooser() {
}
@Override
public void approveSelection() {
JProgressBar progBar = new JProgressBar();
progBar.setIndeterminate(true);
Window win = SwingUtilities.getWindowAncestor(this);
progressDialog = new JDialog(win, "Checking File", ModalityType.APPLICATION_MODAL);
progressDialog.getContentPane().add(progBar);
progressDialog.pack();
progressDialog.setLocationRelativeTo(null);
File file = getSelectedFile();
workOpen = new WorkOpen(file);
workOpen.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (SwingWorker.StateValue.DONE == pcEvt.getNewValue()) {
if (progressDialog != null) {
progressDialog.dispose();
}
try {
boolean bool = workOpen.get().booleanValue();
if (bool) {
superApproveSelection();
} else {
JOptionPane.showMessageDialog(MyFileChooser.this, "Invalid File Chosen");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
workOpen.execute();
progressDialog.setVisible(true);
}
// ****** this is key *****
private void superApproveSelection() {
super.approveSelection();
}
public Document getDocument() {
if (workOpen == null) {
return null;
} else {
return workOpen.getDocument();
}
}
}
class WorkOpen extends SwingWorker<Boolean, Void> {
private static final long SLEEP_TIME = 4 * 1000;
private Document document = null;
private File file = null;
public WorkOpen(File file) {
this.file = file;
}
@Override
protected Boolean doInBackground() throws Exception {
if (file == null || !file.exists()) {
return Boolean.FALSE;
}
Thread.sleep(SLEEP_TIME);
String fileName = file.getName();
if (fileName.contains(".txt")) {
Scanner scan = new Scanner(file);
StringBuilder stringBuilder = new StringBuilder();
while (scan.hasNextLine()) {
stringBuilder.append(scan.nextLine() + "\n");
}
document = new PlainDocument();
document.insertString(0, stringBuilder.toString(), null);
return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Document getDocument() {
return document;
}
}
我的示例的说明和要点:
- 这个例子的行为非常简单。您选择一个文件,如果该文件存在并且名称中包含“.txt”,则它会读取该文档并在 JTextField 中显示文本。
- 否则它会显示一条警告消息但仍显示 JFileChooser。
- 可能是关键点:我已经为我的 MyFileChooser 类提供了一个可以由我的 PropertyChangeListener 调用的私有(private)
superApproveSelection()
方法。这会将 super 的approveSelection()
方法暴露给内部类,这是您遇到的问题之一。 - 调用代码的顺序在我的
approveSelection()
覆盖中非常重要。 - 在此方法中,我首先创建 JProgressBar 及其对话框,但尚未立即显示它。它实际上不必首先创建,但它肯定需要最后显示。
- 我创建了 SwingWorker workOpen,但尚未执行它。
- 在执行 SwingWorker 之前,我将 PropertyChangeListener 添加到 SwingWorker。
- 然后执行我的 SwingWorker
- 然后,我使用不确定的 JProgressBar 显示模态 JDialog。
- 我的 SwingWorker 的结构使其 doInBackground 返回 boolean 值,而不是文档。
- 如果一切正常,我让它创建一个(非常简单)文档,保存文本文件的内容,并设置一个可通过 getter 方法获取的私有(private)“doc”字段,然后让 doInBackground 返回 Boolean.TRUE 如果一切正常。
- 我给我的 doInBackground 一个 Thread.sleep(4000) 只是为了假装它的操作需要很多时间。你的当然不会有这个。
- 在 PropertyChangeListener 中,如果 SwingWorker 完成,我将处理进度条对话框,然后在 SW 上调用
get()
来获取 boolean 结果。 - 如果为 Boolean.TRUE,则调用上述
superApproveSelection()
方法。 - 否则显示错误消息。请注意,由于未调用 super 的approveSelection(),因此文件选择器对话框仍然显示。
- 如果调用approveSelection,则显示文件选择器对话框的代码将获得适当的返回值,从文件选择器中提取文档并在JTextArea 中显示文档。
关于java - 在 SwingWorker 中调用 super.approveSelection(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15672905/