JSpinner.DateEditor必须包括年份,即使开始和结束是同一年

我有一个使用SpinnerDateModel的JSpinner,它的开头是2010年1月1日00:00:00.000结束日期是2010年1月1日00:12:34.217。 我希望我的JSpinner.DateEditor使用格式HH:mm:ss.SSS但旋转器不会使用此格式旋转。 只有在将“yyyy”添加到格式时才会旋转。 我怎么能绕过这个?

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); } } 

在JRE源代码中进行了大量挖掘之后,我发现微调器由文本值而不是真实日期支持。 当您按下向上和向下旋转按钮时,将解析该值,然后将其与最小值和最大值进行比较。 由于您的格式没有一年,因此日期将被解析,年份始终为1970年,即年份偏离0年。 这会导致微调器在您尝试旋转时始终返回超出范围的错误。

最快的解决方案是简单地使用1970作为你的年份而不是2010年。但是,如果你的初始日期是在1970年底,那么微调器就不会让你的用户翻到1971年1月(相反它可能会跳回到开头1970年)。

另一种解决方案可以适应跨越日历年边界的日期。 但是,它并不那么简单(或漂亮)。 在JRE中,当DateFormatter解析日期字符串时,它使用单个String参数构造函数动态地实例化一个类。 该字符串是微调器的日期。 默认情况下,此类是Date或其子类。 我们可以让格式化程序实例化我们自己的Date类,它修复了执行任何日期比较之前的年份。


添加年份的日期类:

 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() ); } } 

使用我们的日期修复手动设置微调器:

 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! 

丑陋,但它的工作原理。

另外,如果你想在JRE源代码中查找,我建议查看InternationalFormatter(DateFormatter的超类stringToValue(String text)的公共方法stringToValue(String text) )。

我不知道为什么这不起作用,但如果你将m2的声明更改为:

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

有用。

这有点难看,但我得到了它的工作。

这是代码。 我只关注jCinner的addChangeListener中的范围validation。

 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); } }