After several years of disappointment with this problem, I implemented a solution that, in my opinion, is the best.
First, why nothing works:
JButton::setMutliclickThreshold() is not really the optimal solution because (as you said) there is no way to know how long to set the threshold. This is only useful for double-click protection for happy end users, because you must set an arbitrary threshold.JButton::setEnabled() is obviously a fragile solution that will make life a lot more complicated.
So, I created SingletonSwingWorker . Now singletones are called anti-patterns, but if they execute correctly, they can be very powerful. Here is the code:
public abstract class SingletonSwingWorker extends SwingWorker { abstract void initAndGo(); private static HashMap<Class, SingletonSwingWorker> workers; public static void runWorker(SingletonSwingWorker newInstance) { if(workers == null) { workers = new HashMap<>(); } if(!workers.containsKey(newInstance.getClass()) || workers.get(newInstance.getClass()).isDone()) { workers.put(newInstance.getClass(), newInstance); newInstance.initAndGo(); } } }
This will allow you to create classes that extend SingletonSwingWorker and ensure that only one instance of this class will be executed at a time. Here is an example implementation:
public static void main(String[] args) { final JFrame frame = new JFrame(); JButton button = new JButton("Click"); button.setMultiClickThreshhold(5); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { DisplayText_Task.runWorker(new DisplayText_Task(frame)); } }); JPanel panel = new JPanel(); panel.add(button); frame.add(panel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } static class DisplayText_Task extends SingletonSwingWorker { JFrame dialogOwner; public DisplayText_Task(JFrame dialogOwner) { this.dialogOwner = dialogOwner; } JDialog loadingDialog; @Override void initAndGo() { loadingDialog = new JDialog(dialogOwner); JProgressBar jpb = new JProgressBar(); jpb.setIndeterminate(true); loadingDialog.add(jpb); loadingDialog.pack(); loadingDialog.setVisible(true); execute(); // This must be put in the initAndGo() method or no-workie } @Override protected Object doInBackground() throws Exception { for(int i = 0; i < 100; i++) { System.out.println(i); Thread.sleep(200); } return null; } @Override protected void done() { if(!isCancelled()) { try { get(); } catch (ExecutionException | InterruptedException e) { loadingDialog.dispose(); e.printStackTrace(); return; } loadingDialog.dispose(); } else loadingDialog.dispose(); } }
In my SwingWorker implementations SwingWorker I like to load a JProgressBar , so I always do this before running doInBackground() . With this implementation, I load a JProgressBar inside the initAndGo() method , and also call execute() , which must be placed in the initAndGo() method or the class will not work .
In any case, I think this is a good solution, and it is not necessary that this code be reorganized to update your applications.
Very interested in feedback on this decision.
ryvantage
source share