Java Tips Weblog

  • Blog Stats

    • 2,572,340 hits
  • Categories

  • Archives

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:

Temporal Spinners

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

Swing and Java 8

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

10 Responses to “Temporal Spinners”

  1. 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.

    • 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

  2. Anonymous said

    Hello Darryl,

    thanks for the code. To compile the demo code without warnings I had to do a

    SpinnerTemporalModel<LocalDate> localDateModel= new SpinnerTemporalModel(...)
    

    And for the demo code to cut & paste and run without a run time error I suggest

      LocalDate today= LocalDate.now();
      SpinnerTemporalModel<LocalDate> localDateModel= new SpinnerTemporalModel
    	      (today, today.withMonth(1), today.withMonth(12), ChronoUnit.DAYS);
    

    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

  3. 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

    SpinnerTemporalModel<LocalDate> localDateModel= new SpinnerTemporalModel<>(...)
    

    And for the demo code to cut & paste and run without a run time error I suggest

      LocalDate today= LocalDate.now();
      SpinnerTemporalModel<LocalDate> localDateModel= new SpinnerTemporalModel<>
    	      (today, today.withMonth(1), today.withMonth(12), ChronoUnit.DAYS);
    

    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

    • 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

  4. 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!

Leave a comment