JSpinner.DateEditor must include year even though start and end is the same year

青春壹個敷衍的年華 提交于 2019-12-01 16:44:58

问题


I have a JSpinner using a SpinnerDateModel which has a start at Jan 1, 2010 00:00:00.000 the end date is Jan 1, 2010 00:12:34.217. I would like my JSpinner.DateEditor to use the format HH:mm:ss.SSS but the spinner doesn't spin with this format. It only spins when "yyyy" is added to the format. How can I get around this?

import java.awt.GridLayout;
import java.util.*;
import javax.swing.*;

public class T extends JPanel {

    public T() {
        super(new GridLayout(2, 2));
        init();
    }

    private void init() {
        Calendar start = GregorianCalendar.getInstance();
        Calendar end = GregorianCalendar.getInstance();
        start.clear();
        end.clear();
        start.set(Calendar.YEAR, 2010);
        end.set(Calendar.YEAR, 2010);
        end.add(Calendar.HOUR_OF_DAY, 12);
        SpinnerDateModel m1 =
                new SpinnerDateModel(start.getTime(), start.getTime(),
                end.getTime(), Calendar.MILLISECOND);
        SpinnerDateModel m2 =
                new SpinnerDateModel(start.getTime(), start.getTime(),
                end.getTime(), Calendar.MILLISECOND);
        JSpinner workingSpinner = new JSpinner(m1);
        workingSpinner.setEditor(
                new JSpinner.DateEditor(workingSpinner,
                "yyyy HH:mm:ss.SSS"));
        JSpinner notWorkingSpinner = new JSpinner(m2);
        notWorkingSpinner.setEditor(
                new JSpinner.DateEditor(notWorkingSpinner,
                "HH:mm:ss.SSS"));
        add(new JLabel("Working"));
        add(workingSpinner);
        add(new JLabel("!Working"));
        add(notWorkingSpinner);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new T());
        frame.pack();
        frame.setVisible(true);
    }
}

回答1:


After a good bit of digging around in the JRE source I discovered the the spinner is backed by a text value instead of a true date. When you hit the up and down spin buttons, the value is parsed and then compared to your min and max values. Because your format does not have a year the dates are parsed with the year always being 1970 which is year offset 0 from the epoch. This causes the spinner to always return an out of range error when you try to spin it.

The quickest solution is to simply use 1970 as your year instead of 2010. However, if your initial date is at the end of 1970 the spinner won't let your users roll over into January of 1971 (instead it may jump back to the beginning of 1970).

The other solution can accomodate dates that span calendar year boundaries. However, it is not as simple (or pretty). In the JRE when the DateFormatter parses the date string, it instantiates a class dynamically using a single String parameter constructor. This string is the date from the spinner. By default, this class is either Date or some subclass of it. We can have the formatter instantiate our own Date class which fixes the year before any date comparisons are performed.


Date class that adds the year:

public static class DateThatAddsYear extends Date {
 public DateThatAddsYear( String time ) {
  super( time );
  Calendar cal = GregorianCalendar.getInstance();
  cal.setTime( this );
  // Jump back to 2010, this needs to be implemented more thoroughly in order 
  // to support dates crossing calendar year boundaries
  cal.set( Calendar.YEAR, 2010 );
  setTime( cal.getTimeInMillis() );
 }
}

Manually set up the spinner, using our date fix:

JSpinner notWorkingSpinner = new JSpinner(m2);
JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(notWorkingSpinner);
DateFormatter formatter = new DateFormatter( format );
notWorkingSpinner.setEditor(dateEditor);
dateEditor.getTextField().setFormatterFactory( new DefaultFormatterFactory( formatter ) );
formatter.setValueClass( DateThatAddsYear.class ); // Tell it to use a different value class!

Ugly, but it works.

Also, If you want to poke around in the JRE source I suggest looking at the public method stringToValue(String text) of InternationalFormatter (superclass of DateFormatter).




回答2:


I am not sure why that is not working but if you change the declaration of m2 to:

SpinnerDateModel m2 = new SpinnerDateModel(); m2.setValue(start.getTime());

it works.




回答3:


This is kind of ugly but I got it working.

Here is the code. I just take care of the range validation inside the addChangeListener for the JSpinner.

import java.awt.GridLayout;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class T extends JPanel {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public T() {
        super(new GridLayout(2, 2));
        init();
    }

    public Calendar end;
    public JSpinner notWorkingSpinner;
    private void init() {
        Calendar start = GregorianCalendar.getInstance();
        end = GregorianCalendar.getInstance();
        start.clear();
        end.clear();

        start.set(Calendar.YEAR, 2010);
        end.set(Calendar.YEAR, 2010);

        end.add(Calendar.HOUR_OF_DAY, 12);
        SpinnerDateModel m1 =
                new SpinnerDateModel(start.getTime(), start.getTime(),
                end.getTime(), Calendar.MILLISECOND);

        SpinnerDateModel m2 = new SpinnerDateModel();
        m2.setValue(start.getTime());

        JSpinner workingSpinner = new JSpinner(m1);
        workingSpinner.setEditor(
                new JSpinner.DateEditor(workingSpinner,
                "yyyy HH:mm:ss.SSS"));
        notWorkingSpinner = new JSpinner(m2);
        notWorkingSpinner.setEditor(
                new JSpinner.DateEditor(notWorkingSpinner,
                "HH:mm:ss.SSS"));

        notWorkingSpinner.addChangeListener(new ChangeListener() {

            @Override
        public void stateChanged(ChangeEvent e) {
            SpinnerModel dateModel = notWorkingSpinner.getModel();
            if(dateModel instanceof SpinnerDateModel){
                Date check = ((SpinnerDateModel)dateModel).getDate();

                Calendar checkCal = GregorianCalendar.getInstance();
                checkCal.setTime(check);
                checkCal.set(Calendar.YEAR, end.get(Calendar.YEAR));
                checkCal.set(Calendar.MONTH, end.get(Calendar.MONTH));
                checkCal.set(Calendar.DAY_OF_MONTH, end.get(Calendar.DAY_OF_MONTH));

                if(checkCal.get(Calendar.HOUR_OF_DAY) == 23){
                    dateModel.setValue(start.getTime());
                } else if(checkCal.getTime().compareTo(end.getTime()) > 0){
                    dateModel.setValue(end.getTime());              
                } 
            }
        }
        });

        add(new JLabel("Working"));
        add(workingSpinner);
        add(new JLabel("!Working"));
        add(notWorkingSpinner);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new T());
        frame.pack();
        frame.setVisible(true);
    }
}


来源:https://stackoverflow.com/questions/3875215/jspinner-dateeditor-must-include-year-even-though-start-and-end-is-the-same-year

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!