2016-06-08 8 views
1

Ich versuche, die Java-Best Practices zu folgen, indem ich keine langen Aufgaben auf dem Hauptthread (EDT). Also plane ich einen SwingWorker mit Modal Dialog. Auf diese Weise blockiert der modale Dialog den Benutzer für alles, bis diese Aufgabe erledigt ist, und ich kann den Status des Dialogs während des Prozesses aktualisieren.Dialog mit Swingworker ist ein Huhn/Ei

Das Problem ist jetzt, dass mit dem modalen Dialog, es blockiert nicht nur die Benutzer, sondern auch nichts nach setVisible

So

aufgerufen wird, wenn ich

dialog.setVisible(true); 
new SwingWorkerTask().execute(); //This does not get called 

tun, und wenn ich

new SwingWorkerTask().execute(); 
dialog.setVisible(true); // Well what the point of setting visible after the fact. 

So, wie blockiere ich Benutzeraktionen und zeige einen Dialog, während die Aufgabe stattfindet?

Danke

+1

Haben Sie eine Anwendung gesehen, die das tut? Öffnet Ihr E-Mail-Client einen Dialog, der Sie daran hindert, etwas zu tun, während er nach E-Mails sucht? Blockiert Ihre Datenbank-GUI Sie bei der Ausführung einer Abfrage mit einem Dialog? Überprüfen Sie, welche gängigen Anwendungen ausgeführt werden, wenn sie lange Aufgaben ausführen, und folgen Sie deren Anweisungen. – RealSkeptic

+0

Tipp: etwas mehr Zeit für die Formatierung Ihres Quelltextes ausgegeben ;-) – GhostCat

+0

Das [JavaDoc von 'SwingWorker # get'] (https://docs.oracle.com/javase/8/docs/api/javax/swing/ SwingWorker.html # get--) beschreibt zum Glück genau Ihren Anwendungsfall. Dein zweites Snippet ist das richtige: Du * starte ** zuerst den SwingWorker und ** dann ** mache den Dialog sichtbar. Beachten Sie, dass dies üblicherweise z. in einigen "actionPerformed" -Methode aufgrund einiger Tasten klicken. Dies wird bereits * auf dem EDT ausgeführt. Der 'execute'-Aufruf erzeugt den Hintergrund-Thread und macht den Dialog sichtbar, um eine weitere Interaktion zu verhindern. – Marco13

Antwort

1

Es ist nur ein Huhn/Ei, wenn Sie es so machen. Sie können alle Swing-Objekte im EDT konstruieren und dann lassen Sie Ihren SwingWorker (oder einen anderen Thread) alle Aktualisierungen steuern, indem Sie EDT anweisen, sie über SwingUtilities.invokeLater(Runnable) auszuführen.

import java.awt.BorderLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import javax.swing.JButton; 
import javax.swing.JDialog; 
import javax.swing.JFrame; 
import javax.swing.JProgressBar; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

public class RudeProgressBar extends JFrame { 

    private JButton button; 

    public RudeProgressBar() { 
     setTitle("Rude Progress Bar"); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setLayout(new BorderLayout()); 

     button = new JButton("Do teh work"); 
     add(button, BorderLayout.SOUTH); 

     button.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       JDialog dialog = new JDialog(RudeProgressBar.this, true); 
       dialog.setTitle("Doing teh work"); 
       dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 
       final JProgressBar progressBar = new JProgressBar(0, 100); 
       dialog.setLayout(new BorderLayout()); 
       dialog.add(progressBar); 
       dialog.setSize(100, 100); 
       dialog.setLocationRelativeTo(RudeProgressBar.this); 
       MyTask task = new MyTask(dialog); 
       task.addPropertyChangeListener(new PropertyChangeListener() { 
        @Override 
        public void propertyChange(PropertyChangeEvent evt) { 
         if ("progress".equals(evt.getPropertyName())) { 
          progressBar.setValue((Integer)evt.getNewValue()); 
         } 
        } 
       }); 
       task.execute(); 
      } 
     }); 

     setSize(200, 200); 
     setLocationRelativeTo(null); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new RudeProgressBar().setVisible(true); 
      } 
     }); 
    } 

    private class MyTask extends SwingWorker<Void, Void> { 

     private final JDialog dialog; 

     public MyTask(JDialog dialog) { 
      this.dialog = dialog; 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      SwingUtilities.invokeLater(new Runnable() { 
       @Override 
       public void run() { 
        dialog.setVisible(true); 
       } 
      }); 

      int progress = 0; 
      for (int i = 0; i < 5; i++) { 
       Thread.sleep(1000); 
       setProgress(progress += 20); 
      }    

      return null; 
     } 

     @Override 
     protected void done() { 
      dialog.setVisible(false); 
      dialog.dispose(); 
     } 
    } 
} 

Wenn Sie besorgt sind, dass die invokeLater Implementierung (innen SwingWorker.doInBackground) könnte nach SwingWorker.done ausgeführt werden soll, setzen Sie einfach den Code in done in einer anderen invokeLater. Indem Sie dies tun, ordnen Sie Ihre Runnable Implementierungen für EDT ein, um sie in bestimmter Reihenfolge auszuführen. Die Warteschlange wird auch dann ausgeführt, wenn diese Methode vom EDT selbst aufgerufen wird.

Beachten Sie, dass, wenn Sie einen Blick auf SwingWorker Implementierung nehmen, werden Sie sehen, dass es auf javax.swing.Timer setzt done() auszuführen und die Timer selbst invokeLater ruft, ruft, so dass es innerhalb done wieder beläuft sich nichts zu tun. Nichts wird jedoch falsch sein, wenn Sie es tun.

+0

Große Antwort .... Vielen Dank – Snake