Deaktivieren JButton, während hintergrund-job, um zu vermeiden, mehrere Klicks
Muss ich halt Benutzer, der mehrere Klicks auf einen JButton während der ersten klicken Sie auf trotzdem ausführen.
Konnte ich kam mit einer Lösung für dieses Problem, aber ich weiß nicht completelly verstehen, warum es funktioniert.
Balg, die ich geschrieben der code (gekürzt auf ein minimum), die funktioniert und die, die nicht arbeiten.
Im ersten Beispiel (gut), wenn Sie starten Sie es und klicken Sie auf die Schaltfläche mehrere Male nur eine Aktion ist, als für das zweite Beispiel (schlecht), wenn Sie klicken Sie mit der Maus mehrere Male erhalten Sie Aktion ausgeführt, mindestens zweimal.
Den zweiten (schlechten) Beispiel einfach nicht verwenden invokeLater () - Methode.
Wo ist der Unterschied im Verhalten kamen aus?
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class TestButtonTask {
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final JButton task = new JButton("Test");
task.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
long t = System.currentTimeMillis();
System.out.println("Action received");
task.setText("Working...");
task.setEnabled(false);
SwingUtilities.invokeLater(new Thread() {
@Override
public void run() {
try {
sleep(2 * 1000);
} catch (InterruptedException ex) {
Logger.getLogger(TestButtonTask.class.getName()).log(Level.SEVERE, null, ex);
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
task.setEnabled(true);
task.setText("Test");
}
});
}
});
}
});
frame.add(task);
frame.pack();
frame.setVisible(true);
} //end main
} //end class
Und nun den "falschen" code
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class TestButtonTask {
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final JButton task = new JButton("Test");
task.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
long t = System.currentTimeMillis();
System.out.println("Action received");
task.setText("Working...");
task.setEnabled(false);
SwingUtilities.invokeLater(new Thread() {
@Override
public void run() {
try {
sleep(2 * 1000);
} catch (InterruptedException ex) {
Logger.getLogger(TestButtonTask.class.getName()).log(Level.SEVERE, null, ex);
}
//SwingUtilities.invokeLater(new Runnable() {
//public void run() {
task.setEnabled(true);
task.setText("Test");
//}
//});
}
});
}
});
frame.add(task);
frame.pack();
frame.setVisible(true);
} //end main
} //end class
Nach Informationen von @kleopatra und @Boris Pavlović hier ist der code, den ich erstellt, die zu funktionieren scheint ziemlich anständig.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class TestButtonTask {
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final JButton task = new JButton("Test");
task.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
task.setText("Working...");
task.setEnabled(false);
SwingWorker worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException ex) {
Logger.getLogger(TestButtonTask.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Event " + evt + " name" + evt.getPropertyName() + " value " + evt.getNewValue());
if ("DONE".equals(evt.getNewValue().toString())) {
task.setEnabled(true);
task.setText("Test");
}
}
});
worker.execute();
}
});
frame.add(task);
frame.pack();
frame.setVisible(true);
} //end main
} //end class
- was auch immer Sie tun, nie schlafen Sie auf den EDT
- Es scheint, dass, während die "Aufgabe" ist, die Ausführung der JButton noch pile-up-Ereignisse und sobald die "Aufgabe" fertig ist, hat ausgeführt.
- alle Art von böse Dinge geschehen können, wenn Sie blockiert den EDT gibt es nicht viel zu verstehen, weil böse impliziert unvorhersehbar wie gut es einfach nicht, und ein happy-coder 🙂
Du musst angemeldet sein, um einen Kommentar abzugeben.
haben Sie zwei Möglichkeiten
1) JButton#setMultiClickThreshhold
2) Sie haben nach split diese Idee mit den zwei getrennten Aktionen im actionListener oder Aktion
javax.swing.Action
(aus-und dealyed vonjavax.swing.Timer
),SwingWorker
oderRunnable#Thread
Okay, hier ist ein code-snippet mit einem Action -
Code:
Gibt es zwei weitere Möglichkeiten.
Können Sie eine Flagge. Legen Sie es bei der Aktion start-und reset-zurück nach Ende. Überprüfen Sie die Flaggen in der
actionPerformed
. WenninProgress==true
einfach nichts tun.Weitere Möglichkeit ist das entfernen der listener und weisen ihn zurück, nachdem die Aktion endet.
Dem richtigen Weg ist mit einem SwingWorker. Wenn der Benutzer auf die Schaltfläche klicken, bevor submmiting einen job, um die
SwingWorker
der Zustand der Schaltfläche geändert werden sollten, um BehinderteJButton#setEnabled(false)
. Nach derSwingWorker
beendet den job-Status der Schaltfläche zurücksetzen aktiviert. Hier ist Oracle tutorial aufSwingWorker
Nach Jahren der Umgang mit der frustration von diesem problem, ich habe implementiert eine Lösung, die ich denke, ist die beste.
Zunächst, warum es gar nicht anders geht:
JButton::setMutliclickThreshold()
ist nicht wirklich eine optimale Lösung, weil (wie Sie sagte) es gibt keine Möglichkeit zu wissen, wie lange auf die Schwelle. Das ist nur gut zum Schutz gegen Doppel-klicken Sie auf happy end-Benutzer, weil Sie haben zu setzen Sie einen beliebigen Schwellenwert ist.JButton::setEnabled()
ist ein offensichtlich fragile Lösung, die nur Sie machen das Leben viel schwieriger.So, ich habe die
SingletonSwingWorker
. Nun, Singletons sind sogenannte anti-patterns, aber, wenn richtig umgesetzt, können Sie sehr mächtig. Hier ist der code:Dadurch können Sie Klassen erstellen, die erweitern
SingletonSwingWorker
und garantiert nur eine Instanz dieser Klasse ausführbar zu einer Zeit. Hier ist ein Beispiel für die Implementierung:In meinem
SwingWorker
Implementierungen, wie ich den ladenJProgressBar
, so dass ich immer das tun vor dem ausführendoInBackground()
. Mit dieser Umsetzung, ich lade dieJProgressBar
innerhalb derinitAndGo()
Methode und ich rufe auchexecute()
gebracht werden muss, in derinitAndGo()
- Methode oder der Klasse wird nicht funktionieren.Anyways, ich denke, das ist eine gute Lösung und es sollte nicht so schwer sein, Refactoring code statten Sie Ihre Anwendungen mit es.
Sehr interessiert an feedback zu dieser Lösung.
SwingWorker::isDone()
ist immer ein guter Weg, um zu bestimmen, ob es ok ist, wieder zu laufen, was nicht immer der Fall (in meiner situation).Beachten Sie, dass, wenn Sie ändern nichts in der Benutzeroberfläche muss Ihr code ausführen auf dem Event Dispatch thread mit invokeLater oder invokeAndWait wenn Sie in einem anderen thread. So zweite Beispiel ist falsch, da Sie versuchen, Sie zu ändern aktivierten Zustand aus einem anderen thread und es kann zu unvorhersehbaren Fehlern.