2012-01-27 2 views
5

как уже объяснялось. Я хочу добиться того, что, когда пользователь редактирует дату в JXDatePicker, он может выбирать, погода он набирает. снова в том же формате, который по умолчанию равен dd.MM.yyyy или просто dd.MM.yy. Когда он использует короткую форму, я хочу, чтобы Picker выбрал нынешний век.JXDatePicker с помощью SimpleDateFormat для форматирования dd.MM.yy в dd.MM.yyyy с текущим столетием

Пример:

27.01.2012 edited to 27.01.10 should result in 27.01.2010 

, а также:

27.01.2012 edited to 27.01.2010 should also result in 27.01.2010 

По умолчанию JXDatePicker Handels его следующим образом:

27.01.2012 edited to 27.01.10 results in 27.01.0010 

Что это на самом деле не так, как я хотел, чтобы он работал. После некоторого короткого исследования я нашел следующий метод в SimpleDateFormat

/** 
* Sets the 100-year period 2-digit years will be interpreted as being in 
* to begin on the date the user specifies. 
* 
* @param startDate During parsing, two digit years will be placed in the range 
* <code>startDate</code> to <code>startDate + 100 years</code>. 
*/ 
public void set2DigitYearStart(Date startDate) 

На первый взгляд это звучит так же, как то, что мне нужно. Поэтому я протестировал его и, к сожалению, он не работал, как я надеялся. Это потому, что я хочу использовать dd.MM.yyyy в качестве формата для отображения дат, а также хочу, чтобы он отображался так же, как в editmode. Например, когда пользователь klicks на дату, например 27.01.2012, я также хочу, чтобы это было так же в editmode, а не только в короткой форме: 27.01.12.

Моя проблема теперь в том, что set2DigitYearStart (Date) несчастливо работает только тогда, когда я решил использовать короткую форму в editmode. Я сделал небольшой пример, чтобы показать этот случай (требуется библиотека SwingX из-за jxdatepicker и может быть найдена here).

public class DatePickerExample extends JPanel 
{ 
    static JFrame frame; 

    public DatePickerExample() 
    { 
    JXDatePicker picker = new JXDatePicker(); 
    JTextField field = new JTextField(10); 

    add(field); 
    add(picker); 

    final Calendar instance = Calendar.getInstance(); 
    instance.set(2012, 01, 26); 
    Date date = instance.getTime(); 
    picker.setDate(date); 

    // SimpleDateFormat format = new SimpleDateFormat("dd.MM.yy");//Works, but I wonna display and edit it with dd.MM.yyyy 
    SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy"); 
    final Date startDate = new Date(0);//01.01.1970 
    format.set2DigitYearStart(startDate); 

    picker.setFormats(format); 
    } 

    public static void main(String[] args) 
    { 
    frame = new JFrame(); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setBounds(400, 400, 400, 400); 
    frame.setLayout(new BorderLayout()); 
    frame.add(new DatePickerExample()); 
    frame.setVisible(true); 
    } 
} 

У кого-то уже было такое же требование и может рассказать мне, как сделать эту работу? Любые идеи приветствуются. Заранее большое спасибо. ymene

+1

* «Я хочу, чтобы Picker выбрал нынешний век». * Ах, готовясь к ошибке Y2.1K раньше, я вижу. ;) –

+0

Ха-ха, меня! Несмотря на то, что в 2012 году нет ничего плохого в том, как будет работать программное обеспечение в будущем! : P – crusam

ответ

2

Я не совсем в курсе JXDatePicker конкретно, но если конкретная функциональность вы хотите имитировать это: Оба входа пользователя 27.01.2010 и 27.01.10 независимо друг от друга, должны привести к 27.01.2010

Тогда это будет работа:

import java.text.DateFormat; 
import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

public class Main { 

    public static void main(String[] args) throws ParseException { 
     String inputLiteralDateYY = "27.01.10"; //Also works with "27.01.97" 
     String inputLiteralDateYYYY = "27.01.2010"; //Also works with "27.01.1997" 

     DateFormat dfYYYY = new SimpleDateFormat("dd.MM.yyyy"); 
     DateFormat dfYY = new SimpleDateFormat("dd.MM.yy"); 


     Date dateFromYY = dfYY.parse(inputLiteralDateYY); 
     Date dateFromYYYY = dfYY.parse(inputLiteralDateYYYY); 

     String outputLiteralDateFromYY = dfYYYY.format(dateFromYY); 
     String outputLiteralDateFromYYYY = dfYYYY.format(dateFromYYYY); 

     System.out.println(outputLiteralDateFromYY); 
     System.out.println(outputLiteralDateFromYYYY); 
    } 
} 

дело в том, что сначала разобрать вход с рисунком «дд.мм.гг.», а затем вернуть его форматирование с «дД.ММ.ГГГГ» узором.

Надеюсь, что это поможет или поможет применить это к вашему сценарию.

+0

извините за мой поздний ответ, но я не уверен, если я могу или должен встать между процессами проверки/форматирования компонентов, хотя я также уже решил перезаписать commitEdit JFormattedTextField, чтобы по-своему отличаться, какой формат подходит использовать. Поэтому я считаю это самым последним решением. Спасибо :) – crusam

5

Final (надеюсь :)

Резюме первого редактирования:

  • DatePickerFormatter уже реализует стратегию подстановок (или CompoundFormat, как это было предложено @Robin)
  • последовательность поиска для синтаксический анализ настраивается по коду клиента
  • Идея состоит в том, чтобы попытаться разобраться с первым (как правило, «самым длинным»), если это не сработает, попробуйте следующее (обычно «не так долго») и так до тех пор пока удались или ParseException брошено
  • за год разборе, SimpleDateFormat есть правила, которые противоречат этому самому длинным первому поиску: она требует, чтобы «уу» проверялась перед «ггггом»
  • делает это в Datepicker имеет нежелательную сторону -эффект от всегда с указанием даты с коротким года формат

причина в том, DatePickerFormatter: она не позволяет указать формат форматирования (просто использует первый). Выход обычай DatePickerFormatter, который поддерживает его (во фрагменте, это жёстко использовать второй):

SimpleDateFormat longFormat = new SimpleDateFormat("dd.MM.yyyy"); 
SimpleDateFormat shortFormat = new SimpleDateFormat("dd.MM.yy"); 
Date startDate = new Date(0);//01.01.1970 
shortFormat.set2DigitYearStart(startDate); 

DatePickerFormatter formatter = new DatePickerFormatter(
// invers sequence for parsing to satisfy the year parsing rules 
     new DateFormat[] {shortFormat, longFormat}) { 

      @Override 
      public String valueToString(Object value) throws ParseException { 
       if (value == null) return null; 
       return getFormats()[1].format(value); 
      } 
     } ; 
DefaultFormatterFactory factory = new DefaultFormatterFactory(formatter); 
picker.getEditor().setFormatterFactory(factory); 

Не совсем уверен, что мы должны поддерживать настройки форматирования в базовом классе. DatePickerFormatter немного странный зверь, не распространяющие InternalFormatter и с процессом поиска будучи немного в конкуренции с FormatterFactory ...

Оригинальное

Это не совсем Datepicker, который обрабатывает его таким образом, это основное форматирование (как уже отмечалось в D1e). Ни один из форматов по умолчанию/ter/s не поддерживает два формата одновременно: видеть, пытаться достичь своей цели с помощью ядра JFormattedTextField :-)

Выход из системы FormatterFactory: позволяет использовать разные форматы , в зависимости от контекста: отображение и редактирование - последнее используется, когда поле фокусируется, первое - в другое время. Как редактор сборщика является JFormattedTextField, вы можете настроить его непосредственно (вместо того, чтобы использовать методы setFormats)

SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy"); 
    SimpleDateFormat editFormat = new SimpleDateFormat("dd.MM.yy"); 

    final Date startDate = new Date(0);//01.01.1970 
    instance.setTime(startDate); 
    editFormat.set2DigitYearStart(instance.getTime()); 
    DefaultFormatterFactory factory = new DefaultFormatterFactory(
      new DatePickerFormatter(new DateFormat[] {format}), 
      new DatePickerFormatter(new DateFormat[] {format}), 
      new DatePickerFormatter(new DateFormat[] {editFormat}) 
      ); 
    picker.getEditor().setFormatterFactory(factory); 

Edit

битья головы после прочтения недавнего ответа Робина (+1!) - наконец, смущающе после нескольких лет и лет, я понимаю, что пытается сделать SwingX 'DatePickerFormatter: это поддержка поисковой цепочки formatters (от более длинного к коротким), самого длинного, используемого после совершения, более короткого, чтобы облегчить набор пользователей ,

К сожалению, это не работает как ожидалось.Принимая во внимание последовательность форматов, больше времени, чтобы короче (и соответствующим образом выполнен с возможностью века):

"yyyy", "yy" 

и с учетом входного

"10" 

чувствует, как передается дальше от первого ко второму, в результате чего

2010 

но нет. Как документально (кто читает ... ленивая справку о мне, кашле ...) в SimpleDateFormat

Года: [...] Для синтаксического анализа, если число букв образца больше, чем 2, год интерпретируются буквально, независимо от количества цифр. Таким образом, используя шаблон «MM/DD/YYYY», «01/11/12» разбирает Яну 11, 12 AD

В конце дня - в DatePickerFormatter пытается поддержать этот поиск, но не успешный - это может считаться проблемой SwingX, в конце концов :-)

+0

kleopatra! Я надеялся, что в него будет добавлен ярлык swingx, я бы привлек ваше внимание: D. Прежде всего, спасибо за ваш ответ и извините, что я не смог прокомментировать это ранее. К сожалению, я уже пробовал это так, как вы предлагали. Я знаю, что это работает отлично. Единственное, что мне не нравится, - это следующее: когда пользователь нажимает на текстовое поле, пока он отображается 27.01.2012, он изменяется в editmode (из-за editformat) до 27.01.12, прежде чем пользователь начнет менять дату самостоятельно , Я бы предпочел, чтобы он все еще показывал 27.01.2012, пока он редактирует его. – crusam

+0

У вас может быть какое-то простое предложение, как этого добиться, или мне действительно нужно перезаписать задействованные JFormattedTextFields для форматирования этих строк самостоятельно? Я также заметил, что я могу добавить несколько форматов в качестве editformat. Но, к сожалению, это тоже не помогло. – crusam

+0

как уже отмечалось в моем ответе: это основное поведение, вне контроля SwingX. На самом деле я не раскопал, но я думаю, что у вас вообще не может быть того и другого - как должен знать форматер, который использовать? Вы _might_ попытаетесь внедрить форматировщик поверх двух форматов .. сообщите нам, когда вы преуспеете, может быть, хороший вклад в SwingX :-) – kleopatra

1

kleopatra уже объяснил, как установить Format в подборщик даты. Для этого варианта использования я применил бы комбинацию CompositeFormat и ParseAllFormat вместо того, чтобы иметь отдельный формат для редактирования и обычного режима, чтобы избежать изменения String при запуске редактирования (как вы уже заметили).

Композитный Формат

Составной формат, как следует из названия, является composite implementation из Format класса, но только для синтаксического анализа. Для форматирования используется один номер Format. Это позволяет пользователю вводить свою дату во многих формах, в то время как она форматируется последовательно, используя один конкретный формат для форматирования.

Вы можете получить это поведение, написав еще один сложный Format. Но в этом случае проще просто использовать функции форматирования/разбора, предлагаемые классом SimpleDateFormat JDK.

import java.text.FieldPosition; 
import java.text.Format; 
import java.text.ParsePosition; 
import java.util.ArrayList; 
import java.util.List; 

/** 
* <p>Composite form of {@link java.text.Format Format}. It uses multiple formats for parsing, and 
* only one format for formatting.</p> 
* 
* <p>A possible use-case is the formatting of user input (e.g. in a {@code JFormattedTextField}). 
* Multiple formats for parsing allows accepting multiple forms of user input without having to 
* write a complicated format.</p> 
*/ 
public class CompositeFormat extends Format { 

    private List<Format> fFormats = new ArrayList<>(); 
    private Format fFormattingFormat; 

    /** 
    * Create a new 
    */ 
    public CompositeFormat() { 
    } 

    /** 
    * Add a format to this composite format 
    * 
    * @param aFormat The format to add 
    */ 
    public void addFormat(Format aFormat) { 
    assertNotNull(aFormat, "You cannot add a null Format"); 
    if (!(fFormats.contains(aFormat))) { 
     fFormats.add(aFormat); 
    } 
    } 

    /** 
    * Remove a format from this composite format 
    * 
    * @param aFormat The format to remove 
    */ 
    public void removeFormat(Format aFormat) { 
    assertNotNull(aFormat, "You cannot remove a null Format"); 
    fFormats.remove(aFormat); 
    updateFormattingFormat(); 
    } 

    /** 
    * Sets <code>aFormat</code> as the format which will be used for formatting the 
    * objects. The format will also be added to the list of available formats. 
    * @param aFormat The format which will be used for formatting 
    */ 
    public void setFormattingFormat(Format aFormat){ 
    assertNotNull(aFormat, "Formatting format may not be null"); 
    addFormat(aFormat); 
    fFormattingFormat = aFormat; 
    } 

    private void assertNotNull(Object aObjectToCheck, String aMessage) { 
    if (aObjectToCheck == null) { 
     throw new NullPointerException(aMessage); 
    } 
    } 

    private void updateFormattingFormat(){ 
    if (!(fFormats.contains(fFormattingFormat))){ 
     fFormattingFormat = null; 
     if (!(fFormats.isEmpty())){ 
     fFormattingFormat = fFormats.iterator().next(); 
     } 
    } 
    } 

    @Override 
    public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { 
    assertNotNull(fFormattingFormat, "Set a formatting format before using this format"); 
    return fFormattingFormat.format(obj, toAppendTo, pos); 
    } 

    @Override 
    public Object parseObject(String source, ParsePosition pos) { 
    if (fFormats.isEmpty()){ 
     throw new UnsupportedOperationException("Add at least one format before using this composite format"); 
    } 
    Format formatToUse = fFormats.iterator().next(); 
    int maxIndex = pos.getIndex(); 
    for (Format format : fFormats) { 
     ParsePosition tempPos = new ParsePosition(pos.getIndex()); 
     tempPos.setErrorIndex(pos.getErrorIndex()); 
     format.parseObject(source, tempPos); 
     if (tempPos.getIndex() > maxIndex){ 
     maxIndex = tempPos.getIndex(); 
     formatToUse = format; 
     if(maxIndex == source.length()){ 
      //found a format which parses the whole string 
      break; 
     } 
     } 
    } 
    return formatToUse.parseObject(source, pos); 
    } 
} 

ParseAllFormat

Обычно для пользовательского ввода вы хотите, чтобы весь пользовательский ввод может быть отформатирован/разобран, чтобы избежать этого, пользователь может ввести строку, которая наполовину правильно. ParseAllFormat - decorator для обычного Format, который выдает ParseException с, когда можно проанализировать только часть String.

import java.text.AttributedCharacterIterator; 
import java.text.FieldPosition; 
import java.text.Format; 
import java.text.ParseException; 
import java.text.ParsePosition; 

/** 
* <p>Decorator for a {@link Format Format} which only accepts values which can be completely parsed 
* by the delegate format. If the value can only be partially parsed, the decorator will refuse to 
* parse the value.</p> 
*/ 
public class ParseAllFormat extends Format { 
    private final Format fDelegate; 

    /** 
    * Decorate <code>aDelegate</code> to make sure if parser everything or nothing 
    * 
    * @param aDelegate The delegate format 
    */ 
    public ParseAllFormat(Format aDelegate) { 
    fDelegate = aDelegate; 
    } 

    @Override 
    public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { 
    return fDelegate.format(obj, toAppendTo, pos); 
    } 

    @Override 
    public AttributedCharacterIterator formatToCharacterIterator(Object obj) { 
    return fDelegate.formatToCharacterIterator(obj); 
    } 

    @Override 
    public Object parseObject(String source, ParsePosition pos) { 
    int initialIndex = pos.getIndex(); 
    Object result = fDelegate.parseObject(source, pos); 
    if (result != null && pos.getIndex() < source.length()) { 
     int errorIndex = pos.getIndex(); 
     pos.setIndex(initialIndex); 
     pos.setErrorIndex(errorIndex); 
     return null; 
    } 
    return result; 
    } 

    @Override 
    public Object parseObject(String source) throws ParseException { 
    //no need to delegate the call, super will call the parseObject(source, pos) method 
    return super.parseObject(source); 
    } 
} 

Сочетание этих двух классов позволяет следующий код

import java.text.Format; 
import java.text.ParseException; 
import java.text.SimpleDateFormat; 

public class FormattingDemo { 

    private static Format createCompositeDateFormat(){ 
    Format formattingFormat = new ParseAllFormat(new SimpleDateFormat("dd.MM.yyyy")); 
    SimpleDateFormat shortFormat = new SimpleDateFormat("dd.MM.yy"); 
    Format otherFormat = new ParseAllFormat(shortFormat); 

    CompositeFormat compositeFormat = new CompositeFormat(); 
    compositeFormat.addFormat(otherFormat); 
    compositeFormat.addFormat(formattingFormat); 
    compositeFormat.setFormattingFormat(formattingFormat); 
    return compositeFormat; 
    } 

    public static void main(String[] args) throws ParseException { 
    Format dateFormat = createCompositeDateFormat(); 
    System.out.println(dateFormat.parseObject("27.01.2010")); 
    System.out.println(dateFormat.parseObject("27.01.10")); 
    System.out.println(dateFormat.parseObject("27.01.2012")); 
    System.out.println(dateFormat.format(dateFormat.parseObject("27.01.2010"))); 
    System.out.println(dateFormat.format(dateFormat.parseObject("27.01.10"))); 
    System.out.println(dateFormat.format(dateFormat.parseObject("27.01.2012"))); 
    } 
} 

приводит следующий вывод

Wed Jan 27 00:00:00 CET 2010 
Wed Jan 27 00:00:00 CET 2010 
Fri Jan 27 00:00:00 CET 2012 
27.01.2010 
27.01.2010 
27.01.2012 

Заметим, что есть небольшой улов, для которого я не нашел достойное решение. Порядок, в котором вы добавляете Format экземпляры в CompositeFormat, также является порядком, в котором они оцениваются для синтаксического анализа.В этом случае вам нужно добавить их в правильном порядке, так как даже new SimpleDateFormat("dd.MM.yyyy"), кажется, принимает входную строку 27.01.10 и может анализировать весь String на Date объект, эквивалентный 27.01.0010.

+0

daaarn .. (см. мое редактирование (+1, надеюсь, это поможет получить это право в SwingX) – kleopatra

+0

@ kleopatra У меня была такая же проблема с разбором лет ... основная причина, по которой я добавил примечание о порядке форматы в композитном формате – Robin