views:

576

answers:

5

I want to use a JFormattedTextField to allow the user to input time duration values into a form. Sample valid values are:

2h 30m
72h 15m
6h
0h

However I am having limited success with this. Can some one please suggest how this can be accomplished? I am OK if this result can be achieved using a JTextField as well.

Thanks!


If it is worth anything, here's my current attempt:

 mFormattedText.setFormatterFactory(
    new DefaultFormatterFactory(
        new DateFormatter(
            new SimpleDateFormat("H mm"))));

This sorta works except that:

  • I cannot get h and m to appear as plain text (I tried escaping) *
  • The number of hours has a max

*: See @nanda's answer

A: 

hmm, what i think is that you can achieve the same goal by, creating three different JTextField for all the three time components, one for the HOUR, MINUTE and Second (if it's included) to get your input... just a thought, you could just concatenate them if you it's necessary... just a thought...

ultrajohn
@ultrajohn, thanks for your input, but its part of the requirements that it has to be in one field, and that the format is as I've described in the question.
bguiz
+1  A: 

Have you tried H'h' mm'm'?

nanda
+1 Works with `InputVerifier`, too.
trashgod
@nanda, Thanks for pointing out how to escape characters (worked), but what didn't work was that I want users to enter `6h 42m`, and that results in `Unparseable date: "6h 42m"`, so I am still stuck. This only works when the user enters something like `6:42:00 AM`, which doesn't work for me since the intent is to enter a time duration.
bguiz
@trashgod, could you suggest how to use `InputVerifier` in this scenario?
bguiz
It' weird... maybe you should share your complete code because this is working with my code (see other answer to see the complete code)
nanda
A: 

Here's an example of using InputVerifier to accommodate multiple input formats.

import java.awt.EventQueue;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.Box;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.text.DateFormatter;
import javax.swing.text.DefaultFormatterFactory;

public class FormattedFields {

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

    FormattedFields() {
        Box form = Box.createVerticalBox();

        form.add(new JLabel("Date & Time:"));
        DateTimeField dtField = new DateTimeField(new Date());
        form.add(dtField);

        form.add(new JLabel("Amount:"));
        JFormattedTextField amtField = new JFormattedTextField(
            NumberFormat.getCurrencyInstance());
        amtField.setValue(100000);
        form.add(amtField);

        JFrame frame = new JFrame();
        frame.add(form);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

class DateTimeField extends JFormattedTextField {

    public DateTimeField() {
        super(DateTimeVerifier.getDefaultFormat());
        this.setInputVerifier(new DateTimeVerifier(this));
    }

    public DateTimeField(Date date) {
        this();
        this.setValue(date);
    }

    @Override
    protected void invalidEdit() {
        if (!this.getInputVerifier().verify(this)) {
            super.invalidEdit();
        }
    }
}

class DateTimeVerifier extends InputVerifier {

    private static List<SimpleDateFormat> validForms =
        new ArrayList<SimpleDateFormat>();


    static {
        validForms.add(new SimpleDateFormat("dd-MMM-yyyy HH'h':mm'm'"));
        validForms.add(new SimpleDateFormat("dd-MMM-yyyy HH:mm"));
    }
    private JFormattedTextField tf;
    private Date date;

    public DateTimeVerifier(JFormattedTextField tf) {
        this.tf = tf;
    }

    @Override
    public boolean verify(JComponent input) {
        boolean result = false;
        if (input == tf) {
            String text = tf.getText();
            for (SimpleDateFormat format : validForms) {
                try {
                    date = format.parse(text);
                    result |= true;
                } catch (ParseException pe) {
                    result |= false;
                }
            }
        }
        return result;
    }

    @Override
    public boolean shouldYieldFocus(JComponent input) {
        if (verify(input)) {
            tf.setValue(date);
            return true;
        } else {
            return false;
        }
    }

    public static SimpleDateFormat getDefaultFormat() {
        return validForms.get(0);
    }
}
trashgod
+1  A: 

The code:

public static void main(String[] args) {
    JFrame jFrame = new JFrame();
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jFrame.setLayout(new BorderLayout());
    jFrame.setPreferredSize(new Dimension(500, 500));

    final JFormattedTextField comp = new JFormattedTextField();
    comp.setFormatterFactory(new DefaultFormatterFactory(new DateFormatter(new SimpleDateFormat(
            "H'h' mm'm'"))));
    comp.setValue(Calendar.getInstance().getTime());

    comp.addPropertyChangeListener("value", new PropertyChangeListener() {

        @Override public void propertyChange(PropertyChangeEvent evt) {
            System.out.println(comp.getValue());

        }
    });

    jFrame.getContentPane().add(comp, BorderLayout.CENTER);

    jFrame.pack();
    jFrame.setVisible(true);
}
nanda
A: 

Since you are dealing with duration and not time, I recommend you to have a look at Joda Time. It contains some fields and classes for duration.

I guess you have to write your own Formatter or Format since you are dealing with both "xh xm", "xh" and "xm".

I also guess it can be hard to let your users type in in the same format as you want to display your durantion, maybe you should let the user type in only the number of minutes. Have a look at Specifying Formatters and Using Formatter Factories.

Maybe my question How to get the same value as the user is seeing from a JFormattedTextField? can be helful too.

Jonas