为了账号安全,请及时绑定邮箱和手机立即绑定

更快地通知 PropertyChangeListener

更快地通知 PropertyChangeListener

梦里花落0921 2021-10-28 16:46:31
所以我正在创建一个 JProgressBar 来显示 CSV 操作的进度,其中每一行都被读取并检查是否在必填(NOT NULL)列中没有空值。为此,我创建了一个 SwingWorker 任务,用于将文件中的行数转换为最大进度值的 100%,并以正确率计算进度。那是 SwingWorker:public static class Task extends SwingWorker<String, Object> {    private int counter;    private double rate;    public Task(int max) {        // Adds the PropertyChangeListener to the ProgressBar        addPropertyChangeListener(             ViewHandler.getExportDialog().getProgressBar());        rate = (float)100/max;        setProgress(0);        counter = 0;    }    /** Increments the progress in 1 times the rate based on maximum */    public void step() {        counter++;        setProgress((int)Math.round(counter*rate));    }    @Override    public String doInBackground() throws IOException {        return null;    }    @Override    public void done() {      Toolkit.getDefaultToolkit().beep();      System.out.println("Progress done.");    }}My PropertyChangeListener,由 JProgressBar 包装器实现:@Override    public void propertyChange(PropertyChangeEvent evt) {        if ("progress".equals(evt.getPropertyName())) {            progressBar.setIndeterminate(false);            progressBar.setValue((Integer) evt.getNewValue());        }    }然后,在我实际使用它的地方,我使用doInBackground()我需要的处理覆盖该方法,调用step()每次迭代。    Task read = new Task(lines) {        @Override            public String doInBackground() throws IOException {                while(content.hasNextValue()) {                step();                // Processing            }            return output.toString();        }   };   read.execute();   return read.get();那么发生了什么:处理工作并成功,然后done()被调用,然后propertyChange()注册两个“状态”事件和一个“进度”事件,将 ProgressBar 的进度从 0% 设置为 100%。由于在事件调度线程上异步通知 PropertyChangeListener,因此在调用任何 PropertyChangeListener 之前可能会发生对 setProgress 方法的多次调用。出于性能目的,所有这些调用都合并为一个仅具有最后一个调用参数的调用。所以,毕竟,我的问题是:我做错了什么吗?如果没有,有没有办法让事件调度线程在 onProgress() 发生时或至少不时通知 PropertyChangeListeners?观察:我正在测试的处理需要 3~5 秒。
查看完整描述

1 回答

?
守着星空守着你

TA贡献1799条经验 获得超8个赞

你的问题在这里:


read.execute();

return read.get();

get()是一个阻塞调用,因此在执行您的工作线程后立即从事件线程调用它会阻塞事件线程和您的 GUI。


相反,它应该done()在 worker 将其 state 属性更改为 之后从回调方法(例如方法)或属性更改侦听器中调用SwingWorker.StateValue.DONE。


例如


import java.awt.*;

import java.awt.event.*;

import java.beans.PropertyChangeEvent;

import java.beans.PropertyChangeListener;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.TimeUnit;


import javax.swing.*;


@SuppressWarnings("serial")

public class TestSwingWorkerGui extends JPanel {

    private JProgressBar progressBar = new JProgressBar(0, 100);

    private Action myAction = new MyAction("Do It!");


    public TestSwingWorkerGui() {

        progressBar.setStringPainted(true); 

        add(progressBar);

        add(new JButton(myAction));

    }


    private class MyAction extends AbstractAction {

        public MyAction(String name) {

            super(name);

        }


        @Override

        public void actionPerformed(ActionEvent e) {

            myAction.setEnabled(false);

            Task read = new Task(30) {

                @Override

                public String doInBackground() throws Exception {

                    int counter = getCounter();

                    int max = getMax();

                    while (counter < max) {

                        counter = getCounter();

                        step();

                        TimeUnit.MILLISECONDS.sleep(200);

                    }

                    return "Worker is Done";

                }

            };

            read.addPropertyChangeListener(new MyPropListener());

            read.execute();

        }

    }


    private class MyPropListener implements PropertyChangeListener {

        @Override

        public void propertyChange(PropertyChangeEvent evt) {

            String name = evt.getPropertyName();

            if ("progress".equals(name)) {

                progressBar.setIndeterminate(false);

                progressBar.setValue((Integer) evt.getNewValue());

            } else if ("state".equals(name)) {

                if (evt.getNewValue() == SwingWorker.StateValue.DONE) {

                    myAction.setEnabled(true);

                    @SuppressWarnings("unchecked")

                    SwingWorker<String, Void> worker = (SwingWorker<String, Void>) evt.getSource();

                    try {

                        String text = worker.get();

                        System.out.println("worker returns: " + text);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    } catch (ExecutionException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }


    private static void createAndShowGui() {

        TestSwingWorkerGui mainPanel = new TestSwingWorkerGui();


        JFrame frame = new JFrame("GUI");

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(mainPanel);

        frame.pack();

        frame.setLocationRelativeTo(null);

        frame.setVisible(true);

    }


    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> createAndShowGui());

    }

}

class Task extends SwingWorker<String, Void> {


    private int counter;

    // private double rate;

    private int max;


    public Task(int max) {

        // Adds the PropertyChangeListener to the ProgressBar

        // addPropertyChangeListener(gui);

        // !!rate = (float)100/max;

        this.max = max;

        setProgress(0);

        counter = 0;

    }


    /** Increments the progress in 1 times the rate based on maximum */

    public void step() {

        counter++;

        int progress = (100 * counter) / max;

        progress = Math.min(100, progress);

        setProgress(progress);

        // setProgress((int)Math.round(counter*rate));

    }


    public int getCounter() {

        return counter;

    }


    public int getMax() {

        return max;

    }


    @Override

    public String doInBackground() throws Exception {

        return null;

    }


    @Override

    public void done() {

      Toolkit.getDefaultToolkit().beep();

      System.out.println("Progress done.");

    }

}


查看完整回答
反对 回复 2021-10-28
  • 1 回答
  • 0 关注
  • 122 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信