如何限制用户在JTextField中输入时间的最佳方法

6
在我的Java应用程序中,有一个字段需要用户输入时间。我知道,我可以简单地将其保留为普通的JTextField,在验证时检查该值等等...
但我认为有比那更好的方法。
例如,如果这是日期相关的问题,那么可以非常方便地使用带有swingx的JXDatePicker。最终,用户选择的日期一定是有效的。
同样,对于时间选择,也一定有更好的方法。如果您了解更好的解决方案,请与我们分享您的知识。
感谢您的任何想法。谢谢!

你有考虑使用时间选择器组件吗?Swing 没有内置的时间选择器(据我所知),但是市面上有一些可用的。缺点可能是它们可能需要使用鼠标(或其他指针设备),而一些用户(应该)更喜欢使用键盘。 - Ole V.V.
4个回答

14

你可以使用 JFormattedTextField,看一下下面的示例。这将创建一个JFormattedTextField,它只接受数字并将它们放在形式为 XXhXXminXXs 的表单中,然后向JFormattedTextField添加一个ActionListener,在那里尝试解析为有效的时间对象当按下ENTER。现在我仅展示了JFormattedTextFieldActionListener的使用,我没有尝试转换或任何转换和解析为有效时间的操作:

输入图像描述

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.text.MaskFormatter;

public class FormattedTextFieldExample {

    public FormattedTextFieldExample() {
        initComponents();
    }

    private void initComponents() {
        JFrame frame = new JFrame("JFormattedTextField Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MaskFormatter mask = null;
        try {
            mask = new MaskFormatter("##h##min##s");//the # is for numeric values
            mask.setPlaceholderCharacter('#');
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //
        // Create a formatted text field that accept a valid time.
        //
        final JFormattedTextField timeField = new JFormattedTextField(mask);

        //Add ActionListener for when enter is pressed
        timeField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                Object source = ae.getSource();
                if (source == timeField) {
                    //parse to a valid time here
                    System.out.println(timeField.getText());
                }
            }
        });

        frame.add(timeField);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new FormattedTextFieldExample();
            }
        });
    }
}

2
谢谢 @DavidKroukamp,非常棒的建议。我会尝试并反馈我的意见。以前不知道 JFormattedTextField。非常感谢!! - Anubis
不要毁掉David的优秀答案,但是它没有验证这些值,但这取决于个人需求 ;) - MadProgrammer

7

我之前做过一个TimeField的原型,它仍需要一点工作,但概念非常基本。

它基本上呈现两个JTextFields,一个用于小时,另一个用于分钟,并使它们看起来像是一个单一的字段。

通过使用一些DocumentFilter,它限制了用户的输入。

enter image description here

我之前做过这个,它还需要一些工作,但基本思路已经有了...如果你遇到问题,你将需要自己设法解决;)

/**
 *
 * @author MadProgrammer
 */
public class TimeField extends javax.swing.JPanel {

  // The time of day...
  public enum TimeOfDay {

    AM,
    PM
  }
  private HourDocumentFilter hourDocumentFilter;
  private MinuteDocumentFilter minDocumentFilter;
  private HourKeyHandler hourKeyHandler;
  private MinuteKeyHandler minuteKeyHandler;
  private HourFocusHandler hourFocusHandler;
  private MinuteFocusHandler minuteFocusHandler;
  private boolean use24HourClock;
  private ActionHandler actionHandler;

  /**
   * Creates new form TimeField
   */
  public TimeField() {
    initComponents();
    pnlFields.setBorder(new CompoundBorder(UIManager.getBorder("TextField.border"),new EmptyBorder(0, 2, 0, 2)));

    set24HourClock(false);
    setTime(new Date());
    fldHour.addKeyListener(new HourKeyHandler());
  }

  @Override
  public void addNotify() {
    super.addNotify();
    // Add all the required functionality to make this thing work...
    ((AbstractDocument) fldHour.getDocument()).setDocumentFilter(getHourDocumentFilter());
    ((AbstractDocument) fldMin.getDocument()).setDocumentFilter(getMinuteDocumentFilter());
    fldHour.addFocusListener(getHourFocusHandler());
    fldMin.addFocusListener(getMinuteFocusHandler());
    fldHour.addKeyListener(getHourKeyHandler());
    fldMin.addKeyListener(getMinuteKeyHandler());
    fldHour.addActionListener(getActionHandler());
    fldMin.addActionListener(getActionHandler());
    cmbTimeOfDay.addActionListener(getActionHandler());
  }

  @Override
  public void removeNotify() {
    // Clean up our listeners...
    ((AbstractDocument) fldHour.getDocument()).setDocumentFilter(null);
    ((AbstractDocument) fldMin.getDocument()).setDocumentFilter(null);
    fldHour.removeFocusListener(getHourFocusHandler());
    fldMin.removeFocusListener(getMinuteFocusHandler());
    fldHour.removeKeyListener(getHourKeyHandler());
    fldMin.removeKeyListener(getMinuteKeyHandler());
    fldHour.removeActionListener(getActionHandler());
    fldMin.removeActionListener(getActionHandler());
    cmbTimeOfDay.removeActionListener(getActionHandler());
    super.removeNotify();
  }

  /**
   * Adds an action listener to the component. Actions are fired when the user
   * presses the enter key
   *
   * @param listener
   */
  public void addActionListener(ActionListener listener) {
    listenerList.add(ActionListener.class, listener);
  }

  public void removeActionListener(ActionListener listener) {
    listenerList.remove(ActionListener.class, listener);
  }

  /**
   * Returns the field that is acting as the hour editor
   *
   * @return
   */
  public JTextField getHourEditor() {
    return fldHour;
  }

  /**
   * Returns the field that is acting as the minute editor
   *
   * @return
   */
  public JTextField getMinuteEditor() {
    return fldMin;
  }

  /**
   * Returns the combo box that provides the time of day selection
   *
   * @return
   */
  public JComboBox getTimeOfDayEditor() {
    return cmbTimeOfDay;
  }

  /**
   * Returns the internal action handler. This handler monitors actions on the
   * individual components and merges them into one.
   *
   * @return
   */
  protected ActionHandler getActionHandler() {
    if (actionHandler == null) {
      actionHandler = new ActionHandler();
    }
    return actionHandler;
  }

  /**
   * Returns the hour key listener
   *
   * @return
   */
  protected HourKeyHandler getHourKeyHandler() {
    if (hourKeyHandler == null) {
      hourKeyHandler = new HourKeyHandler();
    }
    return hourKeyHandler;
  }

  /**
   * Returns the minute key listener
   *
   * @return
   */
  protected MinuteKeyHandler getMinuteKeyHandler() {
    if (minuteKeyHandler == null) {
      minuteKeyHandler = new MinuteKeyHandler();
    }
    return minuteKeyHandler;
  }

  /**
   * Returns the document filter used to filter the hour field
   *
   * @return
   */
  protected HourDocumentFilter getHourDocumentFilter() {
    if (hourDocumentFilter == null) {
      hourDocumentFilter = new HourDocumentFilter();
    }
    return hourDocumentFilter;
  }

  /**
   * Returns the document filter user to filter the minute field
   *
   * @return
   */
  protected MinuteDocumentFilter getMinuteDocumentFilter() {
    if (minDocumentFilter == null) {
      minDocumentFilter = new MinuteDocumentFilter();
    }
    return minDocumentFilter;
  }

  /**
   * Returns the focus listener used to monitor the hour field
   *
   * @return
   */
  protected HourFocusHandler getHourFocusHandler() {
    if (hourFocusHandler == null) {
      hourFocusHandler = new HourFocusHandler();
    }
    return hourFocusHandler;
  }

  /**
   * Used the focus listener used to monitor the minute field
   *
   * @return
   */
  protected MinuteFocusHandler getMinuteFocusHandler() {
    if (minuteFocusHandler == null) {
      minuteFocusHandler = new MinuteFocusHandler();
    }
    return minuteFocusHandler;
  }

  /**
   * Sets the time based on the supplied date
   *
   * @param date
   */
  public void setTime(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    int hour = cal.get(Calendar.HOUR);
    int min = cal.get(Calendar.MINUTE);
    int dayPart = cal.get(Calendar.AM_PM);

    TimeOfDay timeOfDay = TimeOfDay.AM;
    switch (dayPart) {
      case Calendar.PM:
        timeOfDay = TimeOfDay.PM;
        break;
    }

    setTime(hour, min, timeOfDay);
  }

  /**
   * Sets the time based on a 24 hour clock. The field does not need to be in 24
   * hour mode to use this method, the method will automatically correct the
   * hour appropriately.
   *
   * @param hour
   * @param min
   */
  public void setTime(int hour, int min) {
    hour = correctHour(hour);
    min = correctMinute(min);

    TimeOfDay timeOfDay = TimeOfDay.AM;
    if (hour >= 12) {
      timeOfDay = TimeOfDay.PM;
    }

    setTime(hour, min, timeOfDay);
  }

  /**
   * Corrects the minute value to make sure it is within allowable ranges.
   *
   * For example, if you pass in 90 the method, it will automatically correct
   * the value to 30, discard the overflow.
   *
   * This will not effect the hour value...although this might be worth
   * consideration in the future
   *
   * @param min
   * @return
   */
  protected int correctMinute(int min) {
    // Make sure the value is positive.
    // If we were interested in altering the hour value as well, we wouldn't
    // want to do this...
    if (min < 0) {
      min += (min * -2);
    }

    // Correct the minute value....
    if (min > 59) {
      // How many hours fit into this value
      float part = min / 60f;
      part = (float) (part - Math.floor(part)); // Get remainder
      min = (int) (60 * part); // Calculate the number of minutes...
    }
    return min;
  }

  /**
   * Basically, this method will attempt to correct the hour value and bring the
   * it into range of a single day.
   *
   * We are basically going to try and figure out how many parts of the day that
   * the hour falls in and make it equal to a single day...
   *
   * That is, if the hour is 35, it's actually 1.458... days, which is roughly 1
   * day and 11 hours. We are only interested in the 11 hours, cause the date is
   * irrelevant to us
   *
   * @param hour
   * @return
   */
  protected int correctHour(int hour) {
    if (hour < 0) {
      hour += (hour * -2);
    }

    if (hour > 23) {
      float part = hour / 24f;
      part = (float) (part - Math.floor(part));
      hour = (int) (24 * part);
    }
    return hour;
  }

  /**
   * Sets the time value for this field...
   *
   * @param hour
   * @param min
   * @param timeOfDay
   */
  public void setTime(int hour, int min, TimeOfDay timeOfDay) {
    hour = correctHour(hour);
    min = correctMinute(min);

    // Now that we have a correct hour value, we need to know if it will
    // actually fit within the correct part of the day...

    switch (timeOfDay) {
      case AM:
        cmbTimeOfDay.setSelectedIndex(0);
        break;
      case PM:
        cmbTimeOfDay.setSelectedIndex(1);
        break;
    }

    if (!is24HourClock()) {
      if (hour > 12) {
        hour -= 12;
      }
    } else {
      if (hour < 12 && timeOfDay.equals(TimeOfDay.PM)) {
        hour += 12;
      }
    }

    fldHour.setText(pad(Integer.toString(hour), 2));
    fldMin.setText(pad(Integer.toString(min), 2));
  }

  public int getHour() {
    return Integer.parseInt(getHourEditor().getText());
  }

  public int getMinute() {
    return Integer.parseInt(getMinuteEditor().getText());
  }

  public TimeOfDay getTimeOfDay() {
    TimeOfDay tod = null;
    switch (cmbTimeOfDay.getSelectedIndex()) {
      case 0:
        tod = TimeOfDay.AM;
        break;
      case 1:
        tod = TimeOfDay.PM;
        break;
    }
    return tod;
  }

  /**
   * Sets if we should be using 24 or 12 hour clock. This basically configures
   * the time of day field and the validation ranges of the various fields
   *
   * @param value
   */
  public void set24HourClock(boolean value) {
    if (value != use24HourClock) {

      use24HourClock = value;
      cmbTimeOfDay.setVisible(!use24HourClock);

      if (cmbTimeOfDay.getSelectedIndex() == 1) {
        setTime(getHour() + 12, getMinute(), getTimeOfDay());
      }

      invalidate();
      firePropertyChange("24HourClock", !use24HourClock, value);
    }
  }

  /**
   * Returns if this is using a 24 or 12 hour clock
   *
   * @return
   */
  public boolean is24HourClock() {
    return use24HourClock;
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    cmbTimeOfDay = new javax.swing.JComboBox();
    pnlFields = new javax.swing.JPanel();
    lblSeperator = new javax.swing.JLabel();
    fldHour = new javax.swing.JTextField();
    fldMin = new javax.swing.JTextField();

    addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(java.awt.event.FocusEvent evt) {
        doFocusGained(evt);
      }
    });
    setLayout(new java.awt.GridBagLayout());

    cmbTimeOfDay.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"am", "pm"}));
    cmbTimeOfDay.setBorder(null);
    cmbTimeOfDay.setEditor(null);
    cmbTimeOfDay.setOpaque(false);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
    add(cmbTimeOfDay, gridBagConstraints);

    pnlFields.setBackground(new java.awt.Color(255, 255, 255));
    pnlFields.setLayout(new java.awt.GridBagLayout());

    lblSeperator.setText(":");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
    pnlFields.add(lblSeperator, gridBagConstraints);

    fldHour.setBorder(null);
    fldHour.setColumns(2);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    pnlFields.add(fldHour, gridBagConstraints);

    fldMin.setBorder(null);
    fldMin.setColumns(2);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 0;
    pnlFields.add(fldMin, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    add(pnlFields, gridBagConstraints);
  }// </editor-fold>                        

  private void doFocusGained(java.awt.event.FocusEvent evt) {
    fldHour.requestFocus();
  }
  // Variables declaration - do not modify                     
  private javax.swing.JComboBox cmbTimeOfDay;
  private javax.swing.JTextField fldHour;
  private javax.swing.JTextField fldMin;
  private javax.swing.JLabel lblSeperator;
  private javax.swing.JPanel pnlFields;
  // End of variables declaration                   

  /**
   * Moves the focus forward to the next field.
   *
   * This is used to provide "automatic" focus movement
   */
  protected void moveFocusForward() {
    if (fldHour.hasFocus()) {
      fldMin.requestFocus();
    } else if (fldMin.hasFocus()) {
      cmbTimeOfDay.requestFocus();
    }
  }

  /**
   * Moves the focus backwards to the previous field.
   *
   * This is used to provide "automatic" focus movement
   */
  protected void moveFocusBackward() {
    if (fldMin.hasFocus()) {
      fldHour.requestFocus();
    } else if (cmbTimeOfDay.hasFocus()) {
      fldMin.requestFocus();
    }
  }

  /**
   * Fires the action performed event to all registered listeners
   *
   * @param evt
   */
  protected void fireActionPerformed(ActionEvent evt) {
    List<ActionListener> lstListeners = Arrays.asList(listenerList.getListeners(ActionListener.class));
    if (!lstListeners.isEmpty()) {
      Collections.reverse(lstListeners);
      for (ActionListener listener : lstListeners) {
        listener.actionPerformed(evt);
      }
    }
  }

  /**
   * Hour key handler, used to monitor "special" keys for the hour field.
   *
   * This looks for the user pressing the ":" key and the right arrow key in
   * order to perform special navigation
   */
  protected class HourKeyHandler extends KeyAdapter {

    @Override
    public void keyPressed(KeyEvent e) {
      boolean numLock = false;
      try {
        // Get the state of the nums lock
        numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
      } catch (Exception exp) {
      }

      // Move focus forward if the user presses the ":"
      if (e.getKeyCode() == KeyEvent.VK_SEMICOLON && e.isShiftDown()) {
        moveFocusForward();
        // Move focus forward if the user pressed the left arrow key
      } else if ((e.getKeyCode() == KeyEvent.VK_NUMPAD6 && !numLock) || e.getKeyCode() == KeyEvent.VK_RIGHT) {
        // If we are in the last edit position
        if (fldHour.getCaretPosition() >= 2) {
          moveFocusForward();
          // Or we are in the first edit position and the field only contains a single character
        } else if (fldHour.getText().trim().length() == 1 && fldHour.getCaretPosition() == 1) {
          moveFocusForward();
        }
      }
    }
  }

  /**
   * Minute key handler, used to monitor "special" keys for the hour field.
   *
   * This looks for the user pressing the left arrow key in order to perform
   * special navigation
   */
  protected class MinuteKeyHandler extends KeyAdapter {

    @Override
    public void keyPressed(KeyEvent e) {
      boolean numLock = false;
      try {
        numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
      } catch (Exception exp) {
      }

      if ((e.getKeyCode() == KeyEvent.VK_NUMPAD4 && !numLock) || e.getKeyCode() == KeyEvent.VK_LEFT) {
        // Only want to move backwards if we are at the first edit position
        if (fldMin.getCaretPosition() == 0) {
          moveFocusBackward();
        }
      }
    }
  }

  /**
   * Hour field focus handler. This watches for focus lost events a
   * automatically pads the field with a leading "0" if the field is only 1
   * character in length
   */
  protected class HourFocusHandler extends FocusAdapter {

    @Override
    public void focusLost(FocusEvent e) {
      String text = fldHour.getText();
      if (text.length() < 2) {
        text = pad(text, 2);
        fldHour.setText(text);
      }
    }
  }

  /**
   * Minute field focus handler, watches for focus lost events and automatically
   * adds a "0" to the end of the field if it is only 1 character in length
   */
  protected class MinuteFocusHandler extends FocusAdapter {

    @Override
    public void focusLost(FocusEvent e) {
      String text = fldMin.getText();
      if (text.length() < 2) {
        fldMin.setText(text + "0");
      }
    }
  }

  /**
   * The document filter used to filter the hour field.
   */
  protected class HourDocumentFilter extends DocumentFilter {

    @Override
    public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
      System.out.println("insert: offset = " + offset + "; text = " + text);
      super.insertString(fb, offset, text, attr);
    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {

      try {
        boolean isAcceptable = false;
        boolean passOnFocus = false;

        int strLength = text.length();
        // We convert the value here to make sure it's a number...
        int value = Integer.parseInt(text);

        // If the length of the string been replaced is only 1 character
        if (strLength == 1) {
          // If we are at the start of the editing position
          if (offset == 0) {
            // What clock type are we using...
            if (!is24HourClock()) {
              // only accept 0 or 1...
              if (value <= 1) {
                isAcceptable = true;
              }
            } else if (value <= 2) {
              isAcceptable = true;
            }
            // If we are at the second editing position
          } else if (offset == 1) {
            // Get the preceeding value, should be 0, 1 or 2
            String upperPart = fb.getDocument().getText(0, 1);
            // Convert the value to an int
            int upperValue = Integer.parseInt(upperPart);

            // The acceptable range of values for the given position
            int lowerRange = 0;
            int upperRange = 9;

            // Which clock are we using
            if (is24HourClock()) {
              // If the first value is 2, we can only accept values from 0-3 (20-23)
              if (upperValue == 2) {
                upperRange = 3;
              }
            } else {
              // 12 hour clock
              // If the first value is 1, we can only accept values from 0-2 (10-12)
              if (upperValue == 1) {
                upperRange = 2;
              }
            }

            // Is the value within accpetable range...
            if (value >= lowerRange && value <= upperRange) {
              isAcceptable = true;
            }

            // Pass on focus (only if the value is accepted)
            passOnFocus = true;
          }
        } else {
          // First, we need to trim the value down to a maximum of 2 characters

          // Need to know at what offest...
          // 2 - offset..
          // offset == 0, length = 2 - offset = 2
          // offset == 1, length = 2 - offset = 1
          strLength = 2 - offset;
          String timeText = text.substring(offset, strLength);
          value = Integer.parseInt(timeText);
          // this will only work if we are using a 24 hour clock
          if (value >= 0 && value <= 23) {
            while (value > 12 && is24HourClock()) {
              value -= 12;
            }

            // Pad out the text if required
            text = pad(value, 2);
            isAcceptable = true;
          }
        }

        if (isAcceptable) {
          super.replace(fb, offset, length, text, attrs);
          if (passOnFocus) {
            moveFocusForward();
          }
        }
      } catch (NumberFormatException exp) {
      }
    }
  }

  /**
   * The document filter used to filter the minute field.
   */
  protected class MinuteDocumentFilter extends DocumentFilter {

    @Override
    public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
      System.out.println("insert: offset = " + offset + "; text = " + text);
      super.insertString(fb, offset, text, attr);
    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {

      try {
        boolean isAcceptable = false;
        boolean passOnFocus = false;

        // How long is the text been added
        int strLength = text.length();
        // Convert the value to an integer now and save us the hassel
        int value = Integer.parseInt(text);

        // If the length is only 1, probably a new character has been added
        if (strLength == 1) {
          // The valid range of values we can accept
          int upperRange = 9;
          int lowerRange = 0;
          if (offset == 0) {
            // If we are at the first edit position, we can only accept values
            // from 0-5 (50 minutes that is)
            upperRange = 5;
          } else if (offset == 1) {
            // Second edit position...
            // Every thing is valid here...
            // We want to pass on focus if the clock is in 12 hour mode
            passOnFocus = !is24HourClock();
          }

          // Is the value acceptable..
          if (value >= lowerRange && value <= upperRange) {
            isAcceptable = true;
          }
        } else {
          // Basically, we are going to trim the value down to at max 2 characters

          // Need to know at what offest...
          // 2 - offset..
          // offset == 0, length = 2 - offset = 2
          // offset == 1, length = 2 - offset = 1
          strLength = 2 - offset;
          String timeText = text.substring(offset, strLength);
          value = Integer.parseInt(timeText);
          if (value >= 0 && value <= 59) {
            // Pad out the value as required
            text = pad(value, 2);
            isAcceptable = true;
          }
        }

        if (isAcceptable) {
          super.replace(fb, offset, length, text, attrs);
          if (passOnFocus) {
            moveFocusForward();
          }
        }

      } catch (NumberFormatException exp) {
      }
    }
  }

  /**
   * This is a simple "pass" on action handler...
   */
  protected class ActionHandler implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
      ActionEvent evt = new ActionEvent(TimeField.this, e.getID(), e.getActionCommand(), e.getModifiers());

      fireActionPerformed(evt);
    }
  }

  public static String pad(long lValue, int iMinLength) {
    return pad(Long.toString(lValue), 2);
  }

  public static String pad(int iValue, int iMinLength) {
    return pad(Integer.toString(iValue), iMinLength);
  }

  public static String pad(String sValue, int iMinLength) {
    StringBuilder sb = new StringBuilder(iMinLength);
    sb.append(sValue);
    while (sb.length() < iMinLength) {
      sb.insert(0, "0");
    }
    return sb.toString();
  }
}

简单来说,这是为了实现验证。与DavidKroukamp提供的简单解决方案相比,JFormattedTextField 不仅限制您输入数字,还可以输入其他字符,例如99:99。这个实现的目的是在提供简单数据输入要求的同时,提供实时验证功能。

2
感谢 @MadProgrammer。您总是来帮忙!我想测试您的代码。但似乎出了问题。我在 NetBeans 中粘贴了代码,但好像有一些类缺失了。您是否使用了外部库?我该怎么办? - Anubis
@Anubis 好的,我更新了代码以删除依赖项(还添加了一些getter方法来获取小时/分钟/时间;) - MadProgrammer
1
@MadProgrammer,为什么有人要选择这个笨重的实现方式而不是DavidKroukamp提供的简单解决方案呢?此外,在手动输入数字时,它还存在一些错误。我更喜欢DavidKroukamp的方法。 - user1547921
1
@CodenamedSC 我做了一个原型 这是一个概念验证。同意,它需要改进,我也这么说过。使用这样的东西的原因是因为它可以快速重复使用。我也同意DavidKroukamp的解决方案对于它的用途来说非常优雅。如果你觉得那更合适,那就用它吧。这个组件的长期解决方案是为用户提供更好的键盘输入。文档过滤器仍然需要改进,我已经有6个月没有看过它了。这只是一个想法的建议,而不是销售推广 ;) - MadProgrammer
1
@MadProgrammer,我并不是想贬低你的努力。如果让你有这种感觉,我很抱歉。当然,你的代码可能有一些高级用途。但是,David的解决方案更适合这个线程的需求。这就是我的意思。 - user1547921
显示剩余2条评论

7
  • 可以使用多个 JSpinner 实例来选择小时、分钟和秒数
  • 使用一些改进的 JFormattedTextField 来提供即时的用户反馈(例如,当输入无效时立即将背景颜色标记为红色)

4

我认为在多方面使用JSpinner或者JFormattedTextField没有任何理由,这对我来说毫无意义,这些概念并不友好,看看以下内容:


+1 是的,总是明智地把事情落实到实际。这将是一个更好的实现。 - David Kroukamp
1
@mKorbel,也许你是对的。但我发现DavidKroukamp上面的代码非常有用。当然,它非常简单和用户友好。我不明白你的评论。如果您能稍微解释一下为什么这样说,我将不胜感激。我的需求只是一个时间字段。不需要输入日期。(实际上,我使用JXDatePicker在单独的字段中获取日期)。 - Anubis
1
@DavidKroukamp,您是否也同意mKorbel的观点?选择Spinner而不是FormattedTextField有什么原因吗? - Anubis
1
如果您从头开始编写代码并想找到最佳方法,那么 @Anubis 的解决方案会非常棒。但在我看来,您已经有了结构,所以除非您想创建一个全新的项目,否则只需调整您拥有的内容/使用需要改进最少/最容易合并的代码即可。 - David Kroukamp
@Anubis,旋转器将允许您将其限制为24小时或12小时,对于其余的即59分钟(或者我认为是这样)。 - David Kroukamp
@DavidKroukamp 是的,但是你可以通过PropertyChangeListner做同样的事情(限制最大为24或12),(是的,我知道它需要更多的处理能力。这是唯一的原因吗?)不用担心重新开始。我需要知道最合适的实现方式。这将有助于未来... - Anubis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接