web-dev-qa-db-ja.com

「お待ちください」JDialogを表示するSwingスレッドを作成する

問題はこれです:
swingアプリケーションが実行されています。ある時点で、ダイアログがユーザー名とパスワードを挿入して[OK]を押す必要があります。
ユーザーが「OK」を押すと、swingアプリケーションは次の順序で実行します。

  1. 「お待ちください」JDialogを開きます
  2. いくつかの操作を行います(最終的には他のJDialogまたはJOptionPaneを表示します)
  3. 操作が完了したら、「お待ちください」JDialogを閉じます。

これは、okButtonActionPerformed()で記述したコードです。

private void okButtonActionPerformed(Java.awt.event.ActionEvent evt) { 
    //This class simply extends a JDialog and contains an image and a jlabel (Please wait)
    final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);    
    waitDialog.setVisible(true);
    ... //Do some operation (eventually show other JDialogs or JOptionPanes)
    waitDialog.dispose()
}

このコードは、同じスレッドでwaitDialogを呼び出すと、閉じない限りすべてブロックするため、明らかに機能しません。
別のスレッドで実行しようとしました:

private void okButtonActionPerformed(Java.awt.event.ActionEvent evt) { 
    //This class simply extends a JDialog and contains an image and a jlabel (Please wait)
    final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);    
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            waitDialog.setVisible(true);
        }
    });
    ... //Do some operation (eventually show other JDialogs or JOptionPanes)
    waitDialog.dispose()
}

しかし、これは機能しません。waitDialogがすぐに表示されるわけではなく、操作が作業を完了した後であるためです(joptionペインに「ログインしている...」が表示されている場合)。

また、invokeLaterの代わりにinvokeAndWaitを使用しようとしましたが、この場合は例外がスローされます。

Exception in thread "AWT-EventQueue-0" Java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread

どのようにできるのか?

11
user2572526

SwingWorkerを使用してバックグラウンド作業を行い、SwingWorkerのdone()メソッドまたは(私の設定)SwingWorkerに追加されたPropertyChangeListenerのダイアログを閉じることを検討してください。

例えば。、

import Java.awt.BorderLayout;
import Java.awt.Dialog.ModalityType;
import Java.awt.Window;
import Java.awt.event.ActionEvent;
import Java.beans.PropertyChangeEvent;
import Java.beans.PropertyChangeListener;    
import javax.swing.*;

public class PleaseWaitEg {
   public static void main(String[] args) {
      JButton showWaitBtn = new JButton(new ShowWaitAction("Show Wait Dialog"));
      JPanel panel = new JPanel();
      panel.add(showWaitBtn);
      JFrame frame = new JFrame("Frame");
      frame.getContentPane().add(panel);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);

   }
}

class ShowWaitAction extends AbstractAction {
   protected static final long SLEEP_TIME = 3 * 1000;

   public ShowWaitAction(String name) {
      super(name);
   }

   @Override
   public void actionPerformed(ActionEvent evt) {
      SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
         @Override
         protected Void doInBackground() throws Exception {

            // mimic some long-running process here...
            Thread.sleep(SLEEP_TIME);
            return null;
         }
      };

      Window win = SwingUtilities.getWindowAncestor((AbstractButton)evt.getSource());
      final JDialog dialog = new JDialog(win, "Dialog", ModalityType.APPLICATION_MODAL);

      mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals("state")) {
               if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                  dialog.dispose();
               }
            }
         }
      });
      mySwingWorker.execute();

      JProgressBar progressBar = new JProgressBar();
      progressBar.setIndeterminate(true);
      JPanel panel = new JPanel(new BorderLayout());
      panel.add(progressBar, BorderLayout.CENTER);
      panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
      dialog.add(panel);
      dialog.pack();
      dialog.setLocationRelativeTo(win);
      dialog.setVisible(true);
   }
}

ノート:

  • 重要なコンセプトは、すべてを設定し、PropertyChangeListenerを追加し、SwingWorkerを実行し、すべてbeforeモーダルダイアログを表示することです。モーダルダイアログが表示されると、呼び出しコードからのすべてのコードフローが凍結されます(ご存知のとおり)。
  • なぜ私は、doneメソッドを使用するよりもPropertyChangeListenerを好むのですか(エリアスが私がここで投票したきちんとした答えで示されているように)-リスナーを使用すると、懸念がより分離され、結合が緩くなります。このようにして、SwingWorkerはそれを使用しているGUIコードについて何も知る必要がありません。
public void okButtonActionPerformed(ActionEvent e) {

    final JDialog loading = new JDialog(parentComponent);
    JPanel p1 = new JPanel(new BorderLayout());
    p1.add(new JLabel("Please wait..."), BorderLayout.CENTER);
    loading.setUndecorated(true);
    loading.getContentPane().add(p1);
    loading.pack();
    loading.setLocationRelativeTo(parentComponent);
    loading.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
    loading.setModal(true);

    SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
        @Override
        protected String doInBackground() throws InterruptedException 
            /** Execute some operation */   
        }
        @Override
        protected void done() {
            loading.dispose();
        }
    };
    worker.execute();
    loading.setVisible(true);
    try {
        worker.get();
    } catch (Exception e1) {
        e1.printStackTrace();
    }
}
11
elias

上記の回答のバリエーション

これは簡単で複製可能な方法です...

//This code goes inside your button action   
DialogWait wait = new DialogWait();

SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {

    @Override
    protected Void doInBackground() throws Exception {

        //Here you put your long-running process...

        wait.close();
        return null;
    }
};

mySwingWorker.execute();
wait.makeWait("Test", evt);
//end


//Create this class on your project
class DialogWait {

private JDialog dialog;

public void makeWait(String msg, ActionEvent evt) {

    Window win = SwingUtilities.getWindowAncestor((AbstractButton) evt.getSource());
    dialog = new JDialog(win, msg, Dialog.ModalityType.APPLICATION_MODAL);

    JProgressBar progressBar = new JProgressBar();
    progressBar.setIndeterminate(true);
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(progressBar, BorderLayout.CENTER);
    panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
    dialog.add(panel);
    dialog.pack();
    dialog.setLocationRelativeTo(win);
       dialog.setVisible(true);
   }

   public void close() {
       dialog.dispose();
   }
}
1
Gerardo Neto