【问题标题】:Java SwingWorker now to update modelJava SwingWorker 现在更新模型
【发布时间】:2016-04-12 15:44:57
【问题描述】:

我目前正在开发一个小应用程序来显示/保存笔记。关闭应用程序后,每条笔记的内容都存储在我硬盘上的 .properties 文件中。如果您再次打开应用程序,则所有笔记都将加载到应用程序中。

当这个加载过程发生时,我会显示一个带有进度条的对话框,指示过程的状态。此外,我显示了一个 GlassPane,因此用户在加载时无法与 GUI 交互或创建/保存笔记。

以下是我当前加载笔记的代码:

public void load() {

    new Thread() {
        @Override
        public void run() {
            Properties data = new Properties();
            FileInputStream iStream = null;
            File[] files = sortPairs();
            int fileIndex = -1;

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    pnlGlass = new MyGlassPane();
                    infoText = new JTextField();
                    progBar = new JProgressBar(0,100);
                    progBar.setValue(0);
                    progBar.setStringPainted(true);
                    infoBox = new LoadInfoDialog(infoText,progBar);
                    infoBox.setLocationRelativeTo(gui);
                    infoBox.setVisible(true);
                    infoBox.setDefaultCloseOperation(infoBox.DO_NOTHING_ON_CLOSE);
                    pnlGlass.setOpaque(false);
                    pnlGlass.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mousePressed(MouseEvent me) {
                            me.consume();
                        }
                    });
                    gui.setGlassPane(pnlGlass);
                    pnlGlass.setVisible(true);
                    pnlGlass.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                }

            });
            for (File file : files) {
                if (file.isFile()) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    try {
                        iStream = new FileInputStream(file);
                        data.load(iStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (!(iStream == null)) {
                            try {
                                iStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    String title = data.getProperty("title");
                    String text = data.getProperty("text");
                    String erstellt = data.getProperty("date");
                    String fileName = data.getProperty("filename");
                    Note note = new Note(text, title);
                    note.setFileName(fileName);
                    note.setDate(erstellt);
                    fileIndex++;
                    final int fileIndexFinal = fileIndex;
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            progBar.setValue(progBar.getValue() + 100 / fileCount);


                            infoText.setText("Lade Notizen, bitte warten..." + progBar.getValue() + "%");
                            noteModel.addNote(note);
                            System.out.println("Index:" + fileIndexFinal);
                        }

                    });

                }
            }
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    pnlGlass.setVisible(false);
                    infoBox.setVisible(false);

                }
            });
        }
    }.start();

}

我确信我的解决方案相当粗糙。经过一些研究,我发现有一个名为“SwingWorker”的类正是为了这个目的(在后台运行 Taks 并更新 gui)但是,我怎样才能从 SwingWorker 中创建我的笔记(将 Note-Object 添加到我的模型中)?

我知道我可以使用 publish 和 process 方法更新我的进度条,但是如何使用 SwingWorker 创建一个对象?

加载过程应为每个音符执行以下例程:

从 .properties 文件中读取内容 -> 创建注释对象 -> 在 JList 中显示注释 -> 更新进度条。

【问题讨论】:

  • 使用publish/process“发布”加载的笔记,使用PropertyChangeListener不监控进度

标签: java swing


【解决方案1】:

首先定义你的核心工作者...

public class NotesLoader extends SwingWorker<List<Properties>, Properties> {

    @Override
    protected List<Properties> doInBackground() throws Exception {
        List<Properties> notes = new ArrayList<>(25);
        File[] files = new File(".").listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.toLowerCase().endsWith(".properties");
            }
        });

        for (File file : files) {
            int count = 0;
            try (Reader reader = new FileReader(file)) {
                Properties note = new Properties();
                note.load(reader);

                notes.add(note);
                publish(note);
                setProgress((int) ((count / (double) files.length) * 100d));
            }
        }

        return notes;
    }

}

这个简单的扫描当前工作目录中的.properties 文件并尝试一次加载它们。加载时,通过publish方法发送,通过setProgress方法更新进度。

现在,我设计了一个简单的进度面板,看起来像这样......

public class LoadingPane extends JPanel {

    private JLabel label;
    private JProgressBar pb;

    public LoadingPane() {
        setLayout(new GridBagLayout());
        setOpaque(false);

        label = new JLabel("Loading, please wait");
        pb = new JProgressBar();

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        add(label, gbc);
        add(pb, gbc);

        addMouseListener(new MouseAdapter() {
        });
        setFocusable(true);
    }

    public void setProgress(float progress) {
        pb.setValue((int) (progress * 100f));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(new Color(128, 128, 128, 128));
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g2d.dispose();
    }

}

这很重要,因为它有一个setProgress 方法

现在,我们可以结合使用这两者在后台加载笔记,并随着进度的更新安全地更新 UI。

LoadingPane loadingPane = new LoadingPane();
JFrame frame = new JFrame("Testing");
frame.setGlassPane(loadingPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("I'll wait here okay"));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

NotesLoader loader = new NotesLoader() {
    @Override
    protected void process(List<Properties> chunks) {
        System.out.println(chunks.size() + " notes were loaded");
        // Update you model??
    }
};
loader.addPropertyChangeListener(new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        String name = evt.getPropertyName();
        switch (name) {
            case "state":
                switch (loader.getState()) {
                    case STARTED:
                        frame.getGlassPane().setVisible(true);
                        frame.getGlassPane().requestFocusInWindow();
                        break;
                    case DONE:
                        frame.getGlassPane().setVisible(false);
                        try {
                            // Doubt your need the list, but it's good to do this
                            // incase there was an exception which occured during
                            // the running of the worker
                            List<Properties> notes = loader.get();
                        } catch (InterruptedException | ExecutionException ex) {
                            ex.printStackTrace();
                            // Maybe show an error message...
                        }
                        break;
                }
                break;
            case "progress":
                // I like working with normalised values :P
                loadingPane.setProgress(((int)evt.getNewValue() / 100f));
                break;
        }
    }
});

该示例覆盖了 process 方法,因此您可以在加载笔记时获取它们,但同样,您可以等到状态更改为 DONE 并一次获取它们,这取决于您

【讨论】:

  • 感谢您的帮助 MP。我像 2 个月前才开始编码,所以有很多问号,即使在阅读 API 文档之后也是如此。但是你的例子很好,我想我现在明白了。
  • 还有一个问题。为什么要将进度除以 100?
  • 因为我喜欢在标准化进度值 (0-1) 下工作,所以我有很多基于这个概念的代码,但 Java 喜欢在 1-100 下工作
  • 我对这个案子还有另一个问题。一切正常,但还有一个小问题。我将代码以将加载窗格的可见性设置为 false 到“完成”过程中,这样如果加载了所有笔记,加载窗格就会消失。但看起来好像最后一个音符的加载和加载窗格的隐藏是同时发生的。因为我的进度条永远不会达到 100%。我怎样才能使加载窗格在所有内容加载后 1 或 2 秒内消失?
  • 您可以等到progress100,但不能保证100% 表示“已完成”。您可以在doInBackground 方法的末尾放置一个Thread.sleep 以延迟其返回,从而增加最后一次“进度”调用与state 更新之间的时间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多