2012-01-27 17 views
5

như đã giải thích Tôi muốn đạt được, khi người dùng đang chỉnh sửa ngày trong JXDatePicker, anh ấy có thể chọn, thời tiết anh ấy nhập một lần nữa trong cùng một định dạng, theo mặc định dd.MM.yyyy hoặc chỉ dd.MM.yy. Khi anh ta sử dụng dạng ngắn tôi muốn Picker chọn thế kỷ hiện tại.JXDatePicker sử dụng SimpleDateFormat để định dạng dd.MM.yy thành dd.MM.yyyy với thế kỷ hiện tại

Ví dụ:

27.01.2012 edited to 27.01.10 should result in 27.01.2010 

cũng như:

27.01.2012 edited to 27.01.2010 should also result in 27.01.2010 

Theo mặc định, JXDatePicker Handels nó theo cách sau:

27.01.2012 edited to 27.01.10 results in 27.01.0010 

Mà không phải là thực sự là cách tôi muốn nó hoạt động. Sau một số nghiên cứu ngắn, tôi đã tìm thấy Phương pháp sau trong 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) 

Khi xem lần đầu, điều này nghe có vẻ chính xác như những gì tôi cần. Vì vậy, tôi đã thử nghiệm nó và không may nó không hoạt động như tôi hy vọng nó sẽ. Điều này là do tôi muốn sử dụng dd.MM.yyyy làm định dạng để hiển thị ngày và cũng muốn nó được hiển thị như thế trong mã chỉnh sửa. Ví dụ khi người dùng gõ vào một ngày như 27.01.2012, tôi cũng muốn nó giống như vậy trong bản chỉnh sửa, và không chỉ là dạng ngắn: 27.01.12.

Vấn đề của tôi bây giờ là, set2DigitYearStart (Ngày) không may chỉ hoạt động, khi tôi chọn sử dụng shortform trong editmode. Tôi đã làm một ví dụ nhỏ để hiển thị trường hợp này (Thư viện SwingX là bắt buộc, vì jxdatepicker và có thể được tìm thấy là 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); 
    } 
} 

Bất kỳ ai đã có yêu cầu tương tự và có thể cho tôi biết cách thực hiện công việc này? Bất kỳ ý tưởng được chào đón. Cảm ơn bạn rất nhiều trước. ymene

+1

* "Tôi muốn Picker chọn thế kỷ hiện tại." * Ah, chuẩn bị cho lỗi Y2.1K sớm, tôi hiểu rồi. ;) –

+0

Haha, đã cho tôi! Mặc dù năm 2012, không có gì sai khi xem xét cách phần mềm sẽ hoạt động trong tương lai! : P – crusam

Trả lời

2

Tôi không hoàn toàn nhận thức JXDatePicker cụ thể, nhưng nếu các chức năng cụ thể mà bạn muốn mô phỏng là: Cả hai sử dụng đầu vào 2010/01/27 và 27.01.10 độc lập nên kết quả trong 27.01.2010

Sau đó, điều này sẽ công việc:

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

Vấn đề là rằng trước tiên bạn phân tích đầu vào với mẫu "DD.MM.YY" và sau đó trả lại định dạng với mẫu "dd.MM.yyyy".

Hy vọng điều này sẽ giúp hoặc giúp áp dụng điều này cho kịch bản của bạn.

+0

xin lỗi vì trả lời trễ của tôi, nhưng tôi không chắc chắn, nếu tôi có thể hoặc nên ở giữa quá trình xác thực/định dạng thành phần, thì tôi cũng đã xem xét ghi đè lên commitEdit của JFormattedTextField để khác biệt, định dạng nào là thích hợp để sử dụng. Vì vậy, tôi giữ điều này như là giải pháp cuối cùng trong tâm trí. Cảm ơn bạn :) – crusam

5

cuối cùng (hy vọng :)

Tóm tắt các chỉnh sửa đầu tiên:

  • DatePickerFormatter đã thực hiện một chiến lược tìm kiếm (hoặc CompoundFormat, theo đề nghị của @Robin)
  • trình tự tra cứu cho phân tích cú pháp được định cấu hình theo mã khách hàng
  • ý tưởng là thử phân tích cú pháp bắt đầu bằng phương pháp đầu tiên (thường là "dài nhất"), nếu không thành công, hãy thử tiếp theo (thường là "không quá dài") và cho đến khi thành công hoặc parseException được ném
  • để phân tích cú pháp năm, SimpleDateFormat có các quy tắc xung đột với lần tra cứu đầu tiên dài nhất đó: yêu cầu "yy" được thử trước khi "yyyy"
  • làm như vậy trong datePicker có mặt không mong muốn -effect của luôn hiển thị ngày với định dạng năm ngắn

lý do là DatePickerFormatter: nó không cho phép để xác định định dạng định dạng (chỉ đơn giản là sử dụng đầu tiên). Cách ra là một DatePickerFormatter tùy chỉnh, hỗ trợ nó (trong đoạn mã, nó hardcoded sử dụng thứ hai):

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

Không hoàn toàn chắc chắn nếu chúng ta nên hỗ trợ cấu hình định dạng trong lớp cơ sở. Các DatePickerFormatter là một chút lạ con thú, không mở rộng InternalFormatter và với quá trình tra cứu là một chút trong cuộc cạnh tranh với một FormatterFactory ...

gốc

Nó không chính xác datepicker mà xử lý nó theo cách đó, nó định dạng lõi (như D1e đã lưu ý). Không có định dạng mặc định/ter/s hỗ trợ hai định dạng cùng một lúc: để xem, cố gắng đạt được mục tiêu của bạn với một lõi JFormattedTextField :-)

Cách ra có thể là FormatterFactory: nó cho phép sử dụng các định dạng khác nhau , tùy thuộc vào ngữ cảnh: hiển thị và chỉnh sửa - sau này được sử dụng khi trường được lấy nét, trường trước đây ở tất cả các lần khác. Là biên tập viên của bảng chọn một JFormattedTextField, bạn có thể cấu hình nó trực tiếp (thay vì sử dụng các phương pháp 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); 

Sửa

đầu đập sau khi đọc câu trả lời gần đây của Robin (+1) - cuối cùng, bối rối sau nhiều năm và nhiều năm, tôi hiểu SwingX 'DatePickerFormatter đang cố gắng làm gì: đó là hỗ trợ chuỗi tìm kiếm các trình định dạng (từ dài hơn đến ngắn hơn), dài nhất được sử dụng sau khi cam kết, ngắn hơn để giảm bớt sự đánh máy của người dùng .

Thật không may là nó không hoạt động như mong đợi một cách trực giác.Với một chuỗi các định dạng, thời gian để ngắn hơn (và phù hợp cấu hình để thế kỷ này):

"yyyy", "yy" 

và đưa đầu vào

"10" 

cảm thấy như được truyền lại từ đầu tiên thứ hai, kết quả là

2010 

nhưng không phải. Theo tài liệu (người đọc tài liệu ... lười biếng tôi, ho ...) trong SimpleDateFormat

Năm: [...] Để phân tích cú pháp, nếu số lượng mẫu chữ cái lớn hơn 2, năm được diễn giải theo nghĩa đen, bất kể số chữ số. Do đó, hãy sử dụng mẫu phân tích mẫu "MM/dd/yyyy", "01/11/12" đến ngày 11 tháng 1, 12 AD

Vào cuối ngày - vì DatePickerFormatter cố gắng hỗ trợ tra cứu đó nhưng không phải là thành công - điều này có thể được coi là một vấn đề SwingX, sau khi tất cả :-)

+0

kleopatra! Tôi hy vọng với việc đặt một thẻ swingx vào nó, tôi sẽ thu hút sự chú ý của bạn: D. Trước hết, cảm ơn bạn vì câu trả lời của bạn và xin lỗi vì tôi không thể bình luận nó trước đó. Thật không may tôi đã thử nó theo cách bạn đề nghị là tốt. Tôi biết điều này hoạt động hoàn toàn tốt đẹp. Chỉ có điều về nó mà tôi không thích là như sau: Khi người dùng nhấp vào trường văn bản trong khi nó hiển thị 27.01.2012, nó thay đổi trong editmode (vì editformat) thành 27.01.12, trước khi người dùng bắt đầu thay đổi ngày của riêng mình . Tôi muốn nó vẫn hiển thị 27.01.2012, trong khi anh ấy đang chỉnh sửa nó. – crusam

+0

bạn có thể có bất kỳ gợi ý đơn giản nào để đạt được điều này hay tôi thực sự phải ghi đè lên JFormattedTextFields có liên quan để định dạng các chuỗi đó theo cách riêng của tôi không? Tôi cũng nhận thấy tôi có thể thêm một số định dạng như editformat. Nhưng đáng tiếc là didnt giúp một trong hai. – crusam

+0

như đã lưu ý trong câu trả lời của tôi: đó là hành vi cốt lõi, ngoài tầm kiểm soát của SwingX. Đã không thực sự đào, nhưng tôi nghĩ rằng bạn thường không thể có cả hai - làm thế nào các định dạng nên biết sử dụng? Bạn _might_ cố gắng để thực hiện một định dạng trên đầu trang của hai định dạng .. xin vui lòng cho chúng tôi biết khi bạn thành công, có thể là một đóng góp tốt đẹp cho SwingX :-) – kleopatra

1

kleopatra đã được giải thích về cách đặt Format trên bộ chọn ngày. Đối với trường hợp sử dụng này, tôi sẽ áp dụng kết hợp của CompositeFormatParseAllFormat thay vì có định dạng riêng để chỉnh sửa và chế độ thông thường để tránh thay đổi String khi bạn bắt đầu chỉnh sửa (như bạn đã nhận thấy).

composite dạng

Định dạng composite, như tên cho thấy, là một composite implementation của lớp Format nhưng chỉ cho các phân tích cú pháp. Đối với định dạng, nó sử dụng một Format. Điều này cho phép người dùng nhập vào ngày của anh ta/cô ta dưới nhiều hình thức, trong khi nó được định dạng một cách nhất quán bằng cách sử dụng một định dạng cụ thể để định dạng.

Bạn cũng có thể có được hành vi này bằng cách viết thêm Format phức tạp hơn. Nhưng trong trường hợp này, sẽ dễ dàng hơn khi sử dụng chức năng định dạng/phân tích cú pháp được cung cấp bởi lớp SimpleDateFormat của 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

Điển hình cho người dùng nhập vào bạn muốn rằng toàn bộ đầu vào sử dụng có thể được định dạng/phân tích cú pháp để tránh mà người dùng có thể nhập vào một String đó là nửa chính xác. ParseAllFormatdecorator cho số Format thông thường ném ParseException khi chỉ một phần của String có thể được phân tích cú pháp.

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

Sự kết hợp của những cả hai lớp cho phép đoạn mã sau

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

kết quả đầu ra sau đây

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 

Lưu ý rằng có một nhược điểm nhỏ mà tôi không tìm thấy một giải pháp tốt. Thứ tự mà bạn thêm Format trường hợp vào số CompositeFormat cũng là thứ tự mà chúng được đánh giá để phân tích cú pháp.Trong trường hợp này, bạn cần phải thêm chúng theo đúng thứ tự như ngay cả new SimpleDateFormat("dd.MM.yyyy") dường như chấp nhận chuỗi đầu vào 27.01.10 và có thể phân tích cú pháp toàn bộ String thành đối tượng Date tương đương với 27.01.0010.

+0

daaarn .. ho .. xem chỉnh sửa của tôi (+1! hy vọng điều này sẽ giúp bạn có được nó ngay trong SwingX) – kleopatra

+0

@ kleopatra Tôi đã có cùng một vấn đề với phân tích cú pháp của năm ... lý do chính tôi đã thêm ghi chú về thứ tự của định dạng ở định dạng tổng hợp – Robin

Các vấn đề liên quan