java - 在 SwingWorker 中调用 super.approveSelection()

标签 java swing swingworker

我有一个自定义的 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() ?

//更多信息

应该发生什么:

  1. 用户选择并打开文件
  2. JLabel 和 JProgressBar 已更新,开始播放不确定进度。
  3. 如果 makeALongCalcualtion(file) 返回 null,用户会收到错误窗口警告,但 JFileChooser stys 打开,因此可以在错误窗口关闭时再次选择。
  4. 否则,将调用 super.approveSelection(),从而允许关闭选择器。
  5. 已创建文档(但如果出现问题,创建文档的方法将返回 null)。
  6. 如果一切正常,JLabel 会更新并且进度条动画会停止(不确定设置为 false)。
  7. 如果出现问题,会发生与步骤 6 相同的情况,只是 JLabel 中的消息不同。

发生了什么:

  1. 相同
  2. 相同
  3. 相同,但是当 makeALongCalculation(file); 时开始,进度条卡住。
  4. 相同
  5. 相同
  6. 相同,但动画不会停止(因为进度条被卡住),只有卡住的“图片”被删除,进度条返回到之前的状态。
  7. 相同

编辑

我对我的程序做了一些修改,现在有了:

批准选择():

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/

相关文章:

java - 在 JSF 中将 inputText 映射到 Date 对象

java - 使用递归添加分数

java - 在Android中使用canvas和位图绘制8x8的正方形

Java Swing - 位置测量单位?

java - SwingWorker 线程中的多线程

java - 在 java 中使用绝对路径读取 CSV 文件给出异常

java - 将 JDBC 中的数据显示到 JTable 中

java - 检查 JTextField 给定的数据

java - 将方法移动到 SwingWorker

java - 如何将文本字段从主类传递到另一个类