PropertyChangeListener

PropertyChangeListener

因此,我正在创建一个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.");
    }
}


我的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%。

正在发生的事情我认为正在发生的事情(为澄清起见,使用check Hovercraft's answer)在JavaDocs中进行了描述:

由于PropertyChangeListener是在事件调度线程上异步通知的,因此在调用任何PropertyChangeListener之前,可能会对setProgress方法进行多次调用。出于性能目的,所有这些调用仅与最后一个调用参数合并为一个调用。

所以,毕竟,我的问题是:我做错了什么吗?如果不是,是否有办法让事件调度线程在onProgress()发生时或至少不时通知PropertyChangeListeners?

观察:我正在测试的处理需要3到5秒。

最佳答案

您的问题在这里:

read.execute();
return read.get();


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

相反,应在工作程序将其状态属性更改为done()后从诸如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.");
    }
}

关于java - 更快通知PropertyChangeListener,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52672462/

10-10 06:13