Temporal Spinners
Posted by Darryl Burke on April 9, 2015
JSpinner was designed to work with Dates, Lists or Numbers. With the introduction of a new Date/Time API in Java 8, wouldn’t it be great to be able to create date or time spinners that use the new classes? SpinnerTemporalModel and SpinnerTemporalEditor make that possible.
The classes of primary interest are LocalDate, LocalDateTime, LocalTime, MonthDay, Year and YearMonth, all of which implement the Temporal interface (they also implement TemporalAdjuster and Comparable); hence the choice of names for these two classes. A picture being worth a thousand words, here are some examples of using a JSpinner with some of the available Temporals:
The visual representation of the Temporal is governed by a DateTimeFormatter passed to the constructor of the SpinnerTemporalEditor. This is the code that creates the first of those spinners (the screenshot was captured on 3 April 2015):
SpinnerModel localDateModel = new SpinnerTemporalModel(LocalDate.now(),
LocalDate.of(2013, 1, 1), LocalDate.of(2015, 12, 31),
ChronoUnit.DAYS);
JSpinner localDateSpinner = new JSpinner(localDateModel);
localDateSpinner.setEditor(new SpinnerTemporalEditor(localDateSpinner,
DateTimeFormatter.ofPattern("d MMMM, uuuu")));
Please note that you will need JDK8 to compile and use these classes.
Get The Code
SpinnerTemporalModel.java
SpinnerTemporalEditor.java
See Also
Related Reading
The Oracle Tutorials: Trail: Date Time
Java API: java.time.temporal.Temporal
Java API: java.time.format.DateTimeFormatter
Java API: java.time.format.FormatStyle
Rob Spoor said
I see two mistakes in SpinnerTemporalModel:
1) It should be [tt]SpinnerTemporalModel<T extends Temporal & TemporalAdjuster & Comparable>[/tt] instead of [tt]SpinnerTemporalModel[/tt]. Besides that, [tt]setMin[/tt] and [tt]setMax[/tt] should take a [tt]T[/tt] instead of a Comparable.
2) [tt]getNextValue()[/tt] and [tt]getPreviousValue()[/tt] should return [tt]null[/tt] if the minimum/maximum value is exceeded (as specified by SpinnerModel), not the min/max.
Darryl Burke said
Thank you, Rob. The class is already defined as
public class SpinnerTemporalModel
extends AbstractSpinnerModel { ... }
Did you see something different?
setMin/Max(…) take a Comparable for compatibility with the related editor’s private formatter class, which extends InternationalFormatter. I’m marking those methods as deprecated and adding new methods for setTemporalMin/Max, on the same lines as I had already done for get/setValue.
The actual contracts of SpinnerMode for getNext/PreviousValue(), which I had overlooked, are
The following/preceding element or null if value is the last/first element of the sequence.
So returning null hinges on the current value being already equal to the limiting value.
I’ve made the changes and shall post an update when the new code is available here.
Thanks again, Darryl
Darryl Burke said
Oh! the browser may be eating up the generics (as it evidently did here).
That was
public class SpinnerTemporalModel<T extends Temporal & TemporalAdjuster & Comparable>
extends AbstractSpinnerModel { ... }
Darryl Burke said
The updated code is now available.
Rob Spoor said
Cheers :)
Anonymous said
Hello Darryl,
thanks for the code. To compile the demo code without warnings I had to do a
And for the demo code to cut & paste and run without a run time error I suggest
Last not least, do you see a way to adopt your SpinnerTemporalModel to a regular SpinnerDateModel in the way that the advance is not tied to the calendarField’s value? If one creates a SpinnerDateModel with a calendarField of Calendar.DAY_OF_MONTH, then of course clicking on the arrow buttons will advance the day. But if one first clicks/transfers the cursor to one of the month or year characters in the JSpinner’s JFormattedTextField, then this section will be in- or decremented. I deem this feature quite practical.
Regards
Jörg
Jörg said
Please delete my previous post. Browser eating less-than-character problem.
Hello Darryl,
thanks for the code. To compile the demo code without warnings I had to do a
And for the demo code to cut & paste and run without a run time error I suggest
Last not least, do you see a way to adopt your SpinnerTemporalModel to a regular SpinnerDateModel in the way that the advance is not tied to the calendarField’s value? If one creates a SpinnerDateModel with a calendarField of Calendar.DAY_OF_MONTH, then of course clicking on the arrow buttons will advance the day. But if one first clicks/transfers the cursor to one of the month or year characters in the JSpinner’s JFormattedTextField, then this section will be in- or decremented. I deem this feature quite practical.
Regards
Jörg
Darryl Burke said
Thank you, Jörg. The very practical in/decrement by calendar field is a feature not of the SpinnerModel but of a JFormattedTextField used as the JSpinner’s editor. I could not find a simple way to replicate this behavior with the new Date/Time API.
It also puzzled my why the designer should have chosen to provide this feature to the formatted text field; after all, the same behavior, via up/down arrow keys, is not available for a formatted text field that accepts integer values.
Darryl
Anonymous said
Hey, good job and thank’s for the code! I was wondering is there a way to make it increase/decrease the value from where it’s selected? Like in a SpinnerDateModel where you double-click in the month and then you can change that, or year, ect?
That’s very useful for the user to change between dates and I really wanted to make it behave like that, thank’s!
Darryl Burke said
Looks like you didn’t read the comment just above yours, and my reply.
Darryl