2

我有一个定制的 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 可以使用它在程序中实际创建文件的表示(文档对象)。

但是,这不起作用,因为 makeALongCalcualtion(file) 没有在 SwingWorker 方法中调用,这会阻塞 EDT。

为了解决这个问题,我必须在 SwingWorker 中调用 makeALongCalcualtion(file)。我可以毫无问题地将那部分代码移入 SwingWorker,但随后我将不得不(由于我的代码逻辑)将 super.approveSelection() 一起移动。

所以底线是,对于这种特定情况,我如何从 doInBackground() 中调用 super.approveSelection() ?

//更多信息

应该发生什么:

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

发生什么了:

  1. 相同的
  2. 相同的
  3. 一样,但是当 makeALongCalculation(file); 开始,progressBar 冻结。
  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);
                    }
                }
            }
        }
    });
}

摇摆工人:

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();
    }
4

1 回答 1

2

“为了解决这个问题,我必须在 SwingWorker 中调用 makeALongCalcualtion(file)。我可以将这部分代码毫无问题地移入 SwingWorker,但随后我将不得不(由于我的代码逻辑)移动super.approveSelection() 以及它。”

不,一点也不真实。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 类提供了一个私有superApproveSelection()方法,可以由我的 PropertyChangeListener 调用。这会将 super 的approveSelection()方法暴露给内部类,这是您遇到的问题之一。
  • 调用代码的顺序在我的approveSelection()覆盖中很重要。
  • 在这种方法中,我首先创建了我的 JProgressBar 及其对话框,但还没有立即显示它。它确实不必首先创建,但它肯定需要最后显示。
  • 我创建了我的 SwingWorker,workOpen,但还没有执行它。
  • 在执行 SwingWorker之前,我将 PropertyChangeListener 添加到SwingWorker。
  • 然后我执行我的 SwingWorker
  • 然后我用不确定的 JProgressBar 显示我的模态 JDialog。
  • 我的 SwingWorker 的结构使其 doInBackground 返回一个布尔值,而不是一个文档。
  • 如果一切正常,我让它创建一个(非常简单的)文档,其中包含文本文件的内容,并设置一个可通过 getter 方法获得的私有“doc”字段,然后如果一切正常,则让 doInBackground 返回 Boolean.TRUE .
  • 我给我的 doInBackground 一个 Thread.sleep(4000) 只是为了假装它的动作需要很多时间。你的当然不会有这个。
  • 如果 SwingWorker 已完成,则在 PropertyChangeListener 中,我将处理进度条对话框,然后调用get()SW 以获取布尔结果。
  • 如果为 Boolean.TRUE,则调用上述superApproveSelection()方法。
  • 否则显示错误消息。请注意,由于未调用 super 的approveSelection(),因此仍显示文件选择器对话框。
  • 如果调用了approveSelection,那么显示我的文件选择器对话框的代码将获得适当的返回值,将从文件选择器中提取文档并在JTextArea 中显示文档。
于 2013-03-28T02:07:01.600 回答