5

すでに説明したように、ユーザーが JXDatePicker 内で日付を編集しているときに、同じ形式で再度入力する天気を選択できます。これは、デフォルトでは dd.MM.yyyy または単に dd.MM.yy です。 . 彼が短い形式を使用するときは、ピッカーに現在の世紀を選択してもらいたいと思います。

例:

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 は次のように処理します。

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 を使用し、編集モードでもそのように表示したいためです。たとえば、ユーザーが 27.01.2012 のような日付をクリックした場合、短い形式の 27.01.12 だけでなく、編集モードでもそのようにしたいと考えています。

私の問題は、set2DigitYearStart(Date) 残念ながら、編集モードでショートフォームを使用することを選択した場合にのみ機能することです。このケースを示すために小さな例を作成しました (jxdatepicker のために SwingX ライブラリが必要であり、ここで見つけることができます)。

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 );
  }
}

誰かがすでに同じ要件を持っていて、これを機能させる方法を教えてもらえますか? どんなアイデアでも大歓迎です。事前にどうもありがとうございました。イメネ

4

3 に答える 3

5

最終(できれば:)

最初の編集の要約:

  • DatePickerFormatter はすでにルックアップ戦略 (または @Robin が提案する CompoundFormat) を実装しています。
  • 解析のためのルックアップ シーケンスは、クライアント コードによって構成可能です。
  • アイデアは、最初の解析 (通常は「最長」) から始めて解析を試行し、それが失敗した場合は次の解析 (通常は「それほど長くない」) を試行し、成功するか parseException がスローされるまで繰り返します。
  • 年解析の場合、SimpleDateFormat にはその最長の最初のルックアップと競合するルールがあります。「yyyy」の前に「yy」を試す必要があります。
  • datePicker でこれを行うと、日付が常に短い年形式で表示されるという望ましくない副作用があります。

その理由は DatePickerFormatter です。フォーマット形式を指定することはできません(単純に最初のものを使用します)。解決策は、それをサポートするカスタム DatePickerFormatter です (スニペットでは、2 番目を使用するようにハードコーディングされています)。

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がすでに述べたように)。デフォルトの format/ter/s はいずれも同時に 2 つのフォーマットをサポートしていません: 確認するには、コア 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);

編集

Robin の最近の回答 (+1!) を読んだ後のヘッドバンギング - 最後に、恥ずかしいことに何年も何年も経って、SwingX の DatePickerFormatter が何をしようとしているのかを理解しました。コミット後に最も長く使用され、ユーザーによる入力を容易にするために短い方が使用されます。

残念ながら、それは直感的に期待されるようには機能しません。一連のフォーマットが与えられた場合、長いものから短いものへ (そして世紀に合わせて適切に構成されています):

"yyyy", "yy"

与えられた入力

"10"

最初から次へと受け継がれているように感じ、

 2010

しかしそうではありません。SimpleDateFormat で文書化されているように (ドキュメントを読む人 ... 怠惰な私、咳 ...)

年: [ ... ] 解析では、パターン文字の数が 2 を超える場合、年は桁数に関係なく文字どおりに解釈されます。したがって、パターン「MM/dd/yyyy」を使用すると、「01/11/12」は西暦 12 年 1 月 11 日と解析されます。

結局のところ、DatePickerFormatter はそのルックアップをサポートしようとしますが、成功しないため、これは SwingX の問題と見なされる可能性があります :-)

于 2012-01-27T16:49:14.470 に答える
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);
    }
}

問題は、最初に「dd.MM.yy」パターンで入力を解析してから、「dd.MM.yyyy」パターンでフォーマットして返すことです。

これがシナリオにこれを適用するのに役立つか、役立つことを願っています。

于 2012-01-27T14:59:27.643 に答える
1

Formatkleopatraは、日付ピッカーでを設定する方法について既に説明しました。このユースケースでは、編集を開始するときに を変更しないように、編集用と通常モード用に別々の形式を使用する代わりに、CompositeFormatとの組み合わせを適用します (既にお気付きのように)。ParseAllFormatString

複合フォーマット

複合フォーマットは、名前が示すように、クラスの複合実装Formatですが、解析専用です。書式設定には、1 つを使用します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は、の一部しか解析できない場合に sをスローするレギュラーのデコレーターです。FormatParseExceptionString

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

于 2012-08-25T19:24:29.253 に答える