5

GUI が実行されているプラ​​ットフォームでビルド/初期化するのが非常に重い GUI があります。したがって、初期化中に進行状況を更新したいと考えています。

初期化中に特定の場所で更新したいJLabelとJProgressBarを含む装飾されていない小さなJDialogがありますが、イベントディスパッチスレッド(Swingルールに従って)がGUIの構築/初期化に使用されるため、進行状況はもちろんですEDT が再びアイドル状態になるまで (つまり、初期化が完了するまで) 更新されません。

「paintImmediately」を使用して再描画するようになった JProgressBar ですが、JLabel とダイアログ自体に対して適切に機能させることができないようです。これを達成するための簡単な推奨/実証済みの方法はありますか?

乾杯...

編集:私がやろうとしていることの例を追加します。もちろん、大幅に単純化されています。

private JLabel progressLabel;
private JProgressBar progressBar;

public static int main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            showProgressDialog();
            progressLabel.setText("construct 1");

            constructSomeHeavyGUI();

            progressLabel.setText("construct 2");
            progressBar.setValue(33);

            constructSomeMoreHeavyGUI();

            progressLabel.setText("construct 3");
            progressBar.setValue(67);

            constructEvenMoreHeavyGUI();

            progressLabel.setText("done");
            progressBar.setValue(100);

            hideProgressDialog();

            showHeavyGUI();

        }
    });
}

progressBar.setValue()/上記の呼び出しによって発生しprogressLabel.setText()た再描画は、もちろん、EDT がビジーである限りキューに入れられ、途中で更新するのではなく、すべて完了した後に再描画が行われます..

4

4 に答える 4

3

SwingWorkerを使用することで、EDT で JProgressBar を正しく更新し、フリーズやSwingのConcurency の問題を発生させずに更新できることをお勧めします。

を使用する別のオプションがありRunnable#threadますが、GUI へのすべての出力を次のようにラップする必要があります。invokeLater();

例えば:

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class TestProgressBar {

    private static void createAndShowUI() {
        JFrame frame = new JFrame("TestProgressBar");
        frame.getContentPane().add(new TestPBGui().getMainPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowUI();
            }
        });
    }

    private TestProgressBar() {
    }
}

class TestPBGui {

    private JPanel mainPanel = new JPanel();

    public TestPBGui() {
        JButton yourAttempt = new JButton("WRONG attempt to show Progress Bar");
        JButton myAttempt = new JButton("BETTER attempt to show Progress Bar");
        yourAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                yourAttemptActionPerformed();
            }
        });
        myAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                myAttemptActionPerformed();
            }
        });
        mainPanel.add(yourAttempt);
        mainPanel.add(myAttempt);
    }

    private void yourAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        Task task = new Task("Your attempt");
        task.execute();
        progressDialog.setVisible(true);
        while (!task.isDone()) {
        }
        progressDialog.dispose();
    }

    private void myAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        final JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        final JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        final Task task = new Task("My attempt");
        task.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equalsIgnoreCase("progress")) {
                    int progress = task.getProgress();
                    if (progress == 0) {
                        bar.setIndeterminate(true);
                    } else {
                        bar.setIndeterminate(false);
                        bar.setValue(progress);
                        progressDialog.dispose();
                    }
                }
            }
        });
        task.execute();
        progressDialog.setVisible(true);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }
}

class Task extends SwingWorker<Void, Void> {

    private static final long SLEEP_TIME = 4000;
    private String text;

    public Task(String text) {
        this.text = text;
    }

    @Override
    public Void doInBackground() {
        setProgress(0);
        try {
            Thread.sleep(SLEEP_TIME);// imitate a long-running task
        } catch (InterruptedException e) {
        }
        setProgress(100);
        return null;
    }

    @Override
    public void done() {
        System.out.println(text + " is done");
        Toolkit.getDefaultToolkit().beep();
    }
}

編集:

1)別の問題を示しました。なぜフライ/ランタイムで多くのトップレベルコンテナを作成し、必要な数のコンテナのみを作成し、それを再利用するのですかremoveAll()

2)これはおそらくあなたが必要としていたものです。JTable 内のすべてのJProgressBarsかなりアクセス可能で構成可能です。

3) これはあなたのpaintImmediately()です。これが、Progress をJLabelJProgressBar#setValue(int); にペイントせずに代わりに使用する理由です。

于 2011-08-16T13:33:10.113 に答える
2

長時間実行されるコードを別のスレッドに移動し、SwingUtilities.invokeAndWait または invokeLater を使用して GUI を更新します。

于 2011-08-16T13:27:26.070 に答える
2

constructSome*HeavyGUI()問題になるのに十分な時間がかかる可能性がありますが、データモデルの入力が問題である可能性が高くなります代わりに、空の GUI 要素を作成して表示し、1 つ以上のSwingWorkerインスタンスを起動して各要素のデータをマーシャリングします。関連する例がここここにあります。

補遺: 問題がコンポーネントのインスタンス化であり、データ モデルをロードしていない場合は、invokeLater()以下のコメントで提案されているように、呼び出しを連鎖させることができます。多くのコンポーネントをインスタンス化する場合は、flyweight パターンを検討くださいJTableはおなじみの例です。

于 2011-08-16T17:23:40.050 に答える
2

@StanislavLSwingUtilities.invokeLater(...)の提案に従って使用するか、SwingWorkerを使用してください。

以下も参照してください。

于 2011-08-16T13:31:55.417 に答える