6

我有一个使用 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);
    }
}
4

3 回答 3

5

在 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 源代码中闲逛,我建议您查看stringToValue(String text)InternationalFormatter 的公共方法(DateFormatter 的超类)。

于 2010-10-08T21:52:55.753 回答
0

我不确定为什么这不起作用,但如果您将 m2 的声明更改为:

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

有用。

于 2010-10-08T21:28:54.907 回答
0

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

这是代码。我只负责 JSpinner 的 addChangeListener 内的范围验证。

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);
    }
}
于 2010-10-08T22:57:52.417 回答