4

JLabelを含むモーダルダイアログがあります。このダイアログは、長時間実行されるルックアップアクティビティを実行する別のクラスのメソッドを呼び出します。このルックアップクラスで、モーダルダイアログから渡されるJLabelを更新したいと思います。

ルックアッププロセスのコードループから、およびタイマー内からJLabelを更新しようとしました。(モーダルダイアログの実行中は、SwingタイマーのactionPerformedメソッドが呼び出されないことがわかりました。)タイマーを実装してから、ルックアッププロセスを別のスレッドに移動しました。

ルックアップは機能しますが、プロセスが終了するまでJLabelが再描画されることはありません。どんな助けにも感謝します。

関連する方法は次のとおりです。DictionaryTupleは、リストと文字列の2つのフィールドを含むPOJOです。ゲッターとセッターだけですが、dictionaryThreadはtupleListにアクセスできます。dictionaryThreadが終了するまで、ここでは触れないことに注意してください。

public List<DictionaryTuple> findTuples() {
    tupleList = new ArrayList<DictionaryTuple>();
    Thread dictionaryThread = new Thread(this);  // This is the long-running lookup thread
    dictionaryThread.start();

    progressTimer = new Timer();
    progressTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
                       //  progressCaption is updated in dictionaryThread
            synchronized (progressCaption) {
                lblProgress.setText(progressCaption);
            }
            lblProgress.repaint();
            Thread.yield();
        }
    }, 250, 250);

     try {
        dictionaryThread.join();
    } catch (InterruptedException e) {
    } finally {
      progressTimer.cancel();
      lblProgress.setText("");
      progressCaption = "";
    }
    return tupleList;
}

私もこのバリエーションを試しました。その場合、内部のrun()呼び出しは、処理が完了するまで保持されます。

    progressTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            synchronized (progressCaption) {
            System.out.println("Fire!");
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Update");
                    lblProgress.setText(progressCaption);
                    lblProgress.repaint();
                }});
            }
        }
    }, 250, 250);
4

3 に答える 3

2

ダイアログを表示に設定した場所を正確に示す必要がありますfindTuples()。メソッドを呼び出す前ですか、それとも後ですか。これは重要です。

提案:

  • SwingイベントスレッドまたはEDT(イベントディスパッチスレッド)のJLabelを常に更新してください。
  • Swingアプリのコンポーネントを更新するためにjava.util.Timerを使用しないでください。
  • javax.swing.Timerを使用してSwingコンポーネントを更新できますが、バックグラウンドプロセスのポーリングに使用しない限り、ここでは機能しません。
  • バックグラウンドスレッドにSwingWorkerを使用することを検討してから、ワーカーの公開/処理メソッドまたはSwingWorkerに追加されたPropertyChangeListenerを介してJLabelを更新してください。
  • ダイアログを表示に設定するに、ダイアログの実行中に実行するすべてのメソッドを呼び出します。
于 2012-07-03T20:14:25.183 に答える
2

このデモは役立つはずです。runを呼び出して長いジョブを実行する場合と、別のスレッドで実行する場合の違いを確認してください


    public static void modalDialogTest() {
        JDialog dialog = new JDialog((JFrame)null);
        final JLabel jl = new JLabel("Need some space");
        JButton jb = new JButton ("Click me");
        Timer t = new Timer(1000, new AbstractAction() {
            int it = 0;
            @Override
            public void actionPerformed(ActionEvent arg0) {
                // TODO Auto-generated method stub
                jl.setText("" + it);
                jl.repaint();
                it++;
            }

        });
        t.start();

        AbstractAction wait = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Runnable r = new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                //r.run();
                new Thread(r).start();
            }
        };

        jb.addActionListener(wait);
        dialog.getContentPane().add(jl);
        dialog.getContentPane().add(jb);
        dialog.getContentPane().setLayout(new FlowLayout());
        dialog.pack();
        dialog.setVisible(true);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    }
于 2012-07-03T20:25:42.660 に答える
1

ご提案ありがとうございます。私が実装したのは、SwingWorkerを使用するためのHovercraftのものです。

私のメソッドが置かれているDictionaryクラスは、SwingWorkerを拡張するようになりました。findTuples()メソッドはdoInBackground()になります。タイマーと私自身のスレッドコードはもう必要ありません。

doInBackground()は、プロパティ名「progress」のpublish()を使用して、更新されたJLabelに表示されるテキストを提供します。

ダイアログコードに戻ると、呼び出しには、Dictionaryインスタンスの作成と、PropertyChangeListenerを使用したJLabel(lblProgressという名前)の変更の処理、および処理が完了したことの検出が含まれます。

private void myCallingMethod() {
    final Dictionary dictionary = new Dictionary();

    dictionary.addPropertyChangeListener(new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent event) {
            List<DictionaryTuple> tupleList;

            // Is processing complete?
            if (event.getNewValue() instanceof StateValue
                    && event.getNewValue() == StateValue.DONE) {
                try {
                    tupleList = dictionary.get();
                    lblProgress.setText(tupleList.size() + " Tuples Found");
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(
                            null,
                            "Error in processing dictionary: "
                                    + e.getMessage());
                    tupleList = new ArrayList<DictionaryTuple>();
                    lblProgress.setText("");
                }

                // Process the results.
                processTuples(tupleList);

            } else {
                // The process isn't done yet.
                // Is this a request to change lblProgress?
                if (event.getPropertyName().equals("progress")) {  
                    lblProgress.setText((String) event.getNewValue());
                }
            }
        }
    });

    // Start the dictionary lookup to run asynchronously.
    dictionary.execute();
}

SwingWorkerの処理が完了したかどうかを判断するためにSwingWorker.isDone()に依存しないことを学びました。私の場合、PropertyChangeListenerはその状態で2回呼び出されました。1回は最後のpublish()呼び出しの結果で、もう1回はタスクが実際に完了したときです。

于 2012-07-06T12:50:31.237 に答える