【问题标题】:Java Custom SpinnerDateModel to edit only hour and minuteJava 自定义 SpinnerDateModel 仅编辑小时和分钟
【发布时间】:2018-02-16 20:26:48
【问题描述】:

我正在尝试创建一个小时和分钟的 JSpinner 编辑器。 JSpinner 很简单

JSpinner startSpinner = new JSpinner();
SpinnerWindowModel spinnerModel = new SpinnerWindowModel();
startSpinner.setModel(spinnerModel);
JSpinner.DateEditor editor = new JSpinner.DateEditor(startSpinner, "HH:mm");
startSpinner.setEditor(editor);

Spinner WindowModel 是:

public SpinnerWindowModel() {
    super();
    Calendar vCal = Calendar.getInstance();
    vCal.set(Calendar.HOUR_OF_DAY, 8);
    vCal.set(Calendar.MINUTE, 0);
    vCal.set(Calendar.SECOND, 0);
    setValue(vCal.getTime());

    setCalendarField(Calendar.MINUTE);

    vCal.set(Calendar.HOUR_OF_DAY, 6);
    vCal.set(Calendar.MINUTE, 0);
    vCal.set(Calendar.SECOND, 0);
    setStart(vCal.getTime());

    vCal.set(Calendar.HOUR_OF_DAY, 18);
    vCal.set(Calendar.MINUTE, 45);
    vCal.set(Calendar.SECOND, 0);
    setEnd(vCal.getTime());
}

@Override
public Object getPreviousValue() {
    Object obj = super.getPreviousValue();
    int fld = getCalendarField();
    System.out.println("GPV: " + fld);

    return obj; 
}

@Override
public Object getNextValue() {
    Object obj = super.getNextValue();
    int fld = getCalendarField();
    System.out.println("GNV: " + fld);
    return obj;
}

getNext/PreviousValue 这两个方法目前仅用于监控调用方法以及调用它们的字段。使用受限编辑器,这些方法不会被调用

如果我将编辑器更改为更完整的字段集,它会像我预期的那样工作。小时和分钟字段是可编辑且正确的。我需要做些什么来编辑精简编辑器吗?

editor = new JSpinner.DateEditor(startSpinner, "dd/MM/yyyy HH:mm:ss.SS");

【问题讨论】:

    标签: java jspinner


    【解决方案1】:

    所以,当我使用你的模型时,它不起作用,但是当我使用类似的东西时......

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
    
        Date startTime = cal.getTime();
        cal.set(Calendar.HOUR, 23);
        cal.set(Calendar.MINUTE, 59);
        Date endTime = cal.getTime();
    
        System.out.println(startTime);
        System.out.println(endTime);
    
        // The Calendar field seems to get ignored and overriden 
        // by the UI delegate based on what part of the field
        // the cursor is current on (hour or minute)
        SpinnerDateModel model = new SpinnerDateModel(startTime, null, endTime, Calendar.MINUTE);
        JSpinner spinner = new JSpinner(model);
        spinner.setEditor(new JSpinner.DateEditor(spinner, "HH:mm"));
    
        spinner.setValue(startTime);
    

    效果很好

    我此时的“建议”不是使用自定义模型,而是通过所需的工厂方法手动构建模型

    更新...

    由于围绕JSpinnerJForamttedTextField 的复杂性,我放弃了尝试让JSpinner 处理时间。相反,我通常会退回到使用单独的字段来管理状态的复杂性

    我从修改你的模型开始...

    public class SpinnerWindowModel extends SpinnerNumberModel {
    
        private Calendar calendar;
        private int calendarField;
    
        public SpinnerWindowModel(int calendarField, Calendar calendar, int value, int min, int max, int step) {
            super(value, min, max, step);
            this.calendar = calendar;
        }
    
        @Override
        public void setValue(Object value) {
            super.setValue(value);
            System.out.println(value);
        }
    
        public int getCalendarField() {
            return calendarField;
        }
    
        @Override
        public Object getPreviousValue() {
            Object obj = super.getPreviousValue();
            if (obj instanceof Integer)  {
                int fld = getCalendarField();
                calendar.set(fld, (int)obj);
    
                return obj;
            }
    
            return getValue();
        }
    
        @Override
        public Object getNextValue() {
            Object obj = super.getNextValue();
            if (obj instanceof Integer)  {
                int fld = getCalendarField();
                calendar.set(fld, (int)obj);
                
                return obj;
            }
    
            return getValue();
        }
    }
    

    然后将其应用于字段...

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    
    Date startTime = cal.getTime();
    cal.set(Calendar.HOUR, 23);
    cal.set(Calendar.MINUTE, 59);
    Date endTime = cal.getTime();
    
    System.out.println(startTime);
    System.out.println(endTime);
    
    SpinnerWindowModel hourModel = new SpinnerWindowModel(Calendar.HOUR, cal, 0, 0, 23, 1);
    SpinnerWindowModel minuteModel = new SpinnerWindowModel(Calendar.HOUR, cal, 0, 0, 59, 15);
    
    JSpinner hour = new JSpinner(hourModel);
    JSpinner minute = new JSpinner(minuteModel);
    
    add(hour);
    add(new JLabel(":"));
    add(minute);
    

    现在,回到这一点的是,分钟字段不会滚动小时字段。我确实考虑过在分钟模型上使用更改侦听器,但问题是,您并不真正知道该字段是上升还是下降,因此没有简单的方法来确定您应该以哪种方式滚动小时。

    我会扩展这个概念,使用两个单独的JTextFields(应用了适当的DocumentFilters)和一个“滚动”控件。想法是,滚动控制(即向上和向下按钮)会影响聚焦场。然后,您可以在触发滚动控制时提供适当的状态信息,以允许字段确定它们应该做什么以及它们应该如何相互影响。

    其中很多可以汇总到一个模型中,该模型控制一个 Calendar(或者最好是 LocalTime)对象,该对象将更改基础日期值并通知需要更改的字段。

    我相信这种方法可以为您提供所需的控制级别,为小时和分钟提供不同的步速,同时为用户交互提供灵活性(如果我愿意,我可以手动输入数字或通过滚动控制输入数字到)

    更新...终于...

    因此,基于this answer,底层JFormattedTextField 无法验证该值,因为时间格式正在丢弃值的“日期”部分,仅保留此时间,这是最小/最大值的外壳检查失败。

    相反,我们需要将最小/最大值的“日期”部分定义为我们可以控制(并且更容易定义)的东西。这也意味着任何输入都必须限制在这个特定的时间点

    Calendar cal = Calendar.getInstance();
    cal.clear();
    cal.set(1970, Calendar.JANUARY, 0, 0, 0);
    cal.set(Calendar.MILLISECOND, 0);
    
    Date startTime = cal.getTime();
    
    cal.set(1970, Calendar.JANUARY, 23, 59, 0);
    cal.set(Calendar.MILLISECOND, 0);
    Date endTime = cal.getTime();
    
    System.out.println(startTime);
    System.out.println(endTime);
    
    Calendar value = Calendar.getInstance();
    value.set(1970, Calendar.JANUARY, 0, 0, 0);
    value.set(Calendar.MILLISECOND, 0);
    
    System.out.println(value.getTime());
    
    // The Calendar field seems to get ignored and overriden 
    // by the UI delegate based on what part of the field
    // the cursor is current on (hour or minute)
    SpinnerDateModel model = new SpinnerDateModel(value.getTime(), startTime, endTime, Calendar.MINUTE);
    JSpinner spinner = new JSpinner(model);
    spinner.setEditor(new JSpinner.DateEditor(spinner, "HH:mm"));
    spinner.setValue(value.getTime());
    
    add(spinner);
    

    然后让我更新您的模型以支持多个步骤状态...

    public class SpinnerWindowModel extends SpinnerDateModel {
    
        public SpinnerWindowModel() {
            super();
            Calendar cal = Calendar.getInstance();
            cal.clear();
            cal.set(1970, Calendar.JANUARY, 0, 0, 0);
            cal.set(Calendar.MILLISECOND, 0);
    
            Date startTime = cal.getTime();
    
            cal.set(1970, Calendar.JANUARY, 23, 59, 0);
            cal.set(Calendar.MILLISECOND, 0);
            Date endTime = cal.getTime();
    
            System.out.println(startTime);
            System.out.println(endTime);
    
            Calendar value = Calendar.getInstance();
            value.set(1970, Calendar.JANUARY, 0, 0, 0);
            value.set(Calendar.MILLISECOND, 0);
            
            setStart(startTime);
            setEnd(endTime);
            setValue(value.getTime());
            setCalendarField(Calendar.MINUTE);
        }
    
        @Override
        public Object getPreviousValue() {
            int fld = getCalendarField();
    
            Object value = super.getPreviousValue();
            
            if (fld == Calendar.MINUTE) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(getDate());
                cal.add(Calendar.MINUTE, -15);
                value = cal.getTime();
            }
            
            return value;
        }
    
        @Override
        public Object getNextValue() {
            int fld = getCalendarField();
            Object value = super.getNextValue();
            
            if (fld == Calendar.MINUTE) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(getDate());
                cal.add(Calendar.MINUTE, 15);
                value = cal.getTime();
            }
            
            return value;
        }
    }
    

    【讨论】:

    • 我会尝试你的建议。我遇到的问题是我只需要以 15 分钟的间隔增加分钟数。我会尝试其他实例化器,看看是否能得到更好的结果。
    • 说实话,我很久以前就放弃了微调器(除了简单的数字功能),而是开发了something like this - 虽然不完全是你所追求的,但重点是,相反试图将小时和分钟作为单个元素处理,我会使用两个数字微调器,一个代表小时,一个代表分钟,从而允许您在两个单独的模型中定义您的交互
    • 我已经有一段时间没有搞砸了,但是从模糊的记忆中,ui 代表可以做出一堆你可能无法控制的选择(很容易),所以我可能不可能确定编辑器试图增加哪个字段
    • 比我想象的还要糟糕。你的工作,因为你没有设置开始时间。如果我不设置开始时间,我可以让我的模型看起来有效。真正的问题是它不听 startTime 或 stopTime,小时字段只是从 0 旋转到 24。我确实需要找到另一种方法来做到这一点。我已经花了太多时间。这只是一个原型。
    • SpinnerDateModel的第一个参数是开始时间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多