Reputation: 1436
I'm trying to realize a certain pattern for a little DigitalWatch class with minimal interface in java (just a practice).
It has 2 buttons: a mode button and an increment button.
The mode button has a listener which triggers the change of my program's state between "SetHours,SetMinutes,DisplayTime" (which exist as objects due to the pattern, the objects call the specific state-dependend methods in my DigitalWatch class).
My method displayTime()
is something like this:
void displayTime()
{
while (pressed==false)
{
try
{
display.setText(String.format("%02d:%02d", hours, minutes));
display.repaint();
Thread.sleep(1000);
minutes = minutes + 1;
if (minutes > 59)
{
minutes = 0;
hours = hours + 1;
}
if (hours > 23)
{
hours = 0;
}
display.setText(String.format("%02d:%02d", hours, minutes));
display.repaint();
} catch (Exception e)
{
break;
}
}
display.setText(String.format("%02d:%02d", hours, minutes));
display.repaint();
}
But it seems that while this loop is active the button looses its clickability.
So once the "time counts" i am stuck with this state and am no longer able to change the mode. Is there a good way/best practice to maintain the clickability of my modebutton?
Upvotes: 2
Views: 88
Reputation: 11113
In Swing, all events are dispatched on a single thread and since your loop never exits it will block the UI thread and prevent any future events from being handled.
What you need to do is execute your loop in a separate thread and control the state of that thread with the button. We also need to make sure that we run any UI-code on the UI-thread, so you'll need to switch back and forth between your timer thread and the UI thread.
Since you're running a recurring task every second, ScheduledExecutorService.scheduledAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) is a good fit.
Something like this:
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> timerFuture = null;
void toggleTimer()
{
if (timerFuture != null)
{
// Stop the timer
timerFuture.cancel();
timerFuture = null;
} else
{
// Start the timer on the separate thread, run it every second
timerFuture = executor.scheduleAtFixedRate((Runnable) () -> {
minutes = minutes + 1;
if (minutes > 59)
{
minutes = 0;
hours = hours + 1;
}
if (hours > 23)
{
hours = 0;
}
// Update the UI, this needs to be done on the UI thread
SwingUtilities.invokeLater(() -> {
display.setText(String.format("%02d:%02d", hours, minutes));
display.repaint();
});
}, 0, 1, TimeUnit.SECONDS);
}
}
Upvotes: 1