【问题标题】:JavaFX Table Cell FormattingJavaFX 表格单元格格式
【发布时间】:2012-07-09 20:57:24
【问题描述】:
    TableColumn<Event,Date> releaseTime  = new TableColumn<>("Release Time");
    releaseTime.setCellValueFactory(
                new PropertyValueFactory<Event,Date>("releaseTime")
            );

如何更改 releaseTime 的格式?目前它在 Date 对象上调用一个简单的 toString。

【问题讨论】:

    标签: format render cell javafx-2


    【解决方案1】:

    如果您想保留 TableColumn 的排序功能,上述解决方案均无效:如果您将 Date 转换为 String 并在 TableView 中以这种方式显示;该表将按原样对其进行排序(因此不正确)。

    我找到的解决方案是继承 Date 类以覆盖 toString() 方法。不过这里有一个警告:TableView 使用 java.sql.Date 而不是 java.util.Date;所以你需要继承前者。

    import java.text.SimpleDateFormat;
    
    public class CustomDate extends java.sql.Date {
    
        public CustomDate(long date) {
            super(date);
        }
    
        @Override
        public String toString() {
            return new SimpleDateFormat("dd/MM/yyyy").format(this);
        }
    }
    

    表格将调用该方法以打印日期。

    当然,您也需要将 TableColumn 声明中的 Date 类更改为新的子类:

    @FXML
    TableColumn<MyObject, CustomDate> myDateColumn;
    

    将对象属性附加到表格列时也是如此:

    myDateColumn.setCellValueFactory(new PropertyValueFactory< MyObject, CustomDate>("myDateAttr"));
    

    最后,为了清楚起见,这是您在对象类中声明 getter 的方式:

    public CustomDate getMyDateAttr() {
        return new CustomDate(myDateAttr.getTime()); //myDateAttr is a java.util.Date           
    }
    

    由于它在幕后使用 java.sql.Date,我花了一段时间才弄清楚这一点;所以希望这可以为其他人节省一些时间!

    【讨论】:

      【解决方案2】:

      Java FX8 更新:

      (我不确定这是否是该答案的好地方,但我在 JavaFX8 中遇到了问题,并且有些事情发生了变化,例如 java.time 包)

      与之前的答案有些不同: 我将日期类型保留在列上,因此我需要同时使用 cellValueFactory 和 cellFactory。 我制作了一个通用的可重用方法来为所有日期列生成 cellFactory。 我使用 java.time 包的 java 8 日期!但是该方法可以很容易地为 java.util.date 重新实现。

       @FXML
       private TableColumn<MyBeanUi, ZonedDateTime> dateColumn;
      
      @FXML
      public void initialize () {
        // The normal binding to column 
        dateColumn.setCellValueFactory(cellData -> cellData.getValue().getCreationDate());
      
        //.. All the table initialisation and then
        DateTimeFormatter format = DateTimeFormatter .ofLocalizedDate(FormatStyle.SHORT);
        dateColumn.setCellFactory (getDateCell(format));
      
      }
      
      public static <ROW,T extends Temporal> Callback<TableColumn<ROW, T>, TableCell<ROW, T>> getDateCell (DateTimeFormatter format) {
        return column -> {
          return new TableCell<ROW, T> () {
            @Override
            protected void updateItem (T item, boolean empty) {
              super.updateItem (item, empty);
              if (item == null || empty) {
                setText (null);
              }
              else {
                setText (format.format (item));
              }
            }
          };
        };
      }
      

      优点是:

      • 该列使用“java8 Date”键入以避免@Jordan 引发的排序问题
      • “getDateCell”方法是通用方法,可用作所有 Java8 时间类型(Local Zoned 等)的实用函数

      【讨论】:

      • 是的,我的实验似乎表明,即使您使用非 ISO 日期格式(其排序就像它的 String 当然等效(2020-04-23 等)!)这使得列可以按时间顺序排序。
      【解决方案3】:

      我建议使用 Java 泛型来创建可重用的列格式化程序,它接受任何 java.text.Format。这减少了样板代码的数量...

      private class ColumnFormatter<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
          private Format format;
      
          public ColumnFormatter(Format format) {
              super();
              this.format = format;
          }
          @Override
          public TableCell<S, T> call(TableColumn<S, T> arg0) {
              return new TableCell<S, T>() {
                  @Override
                  protected void updateItem(T item, boolean empty) {
                      super.updateItem(item, empty);
                      if (item == null || empty) {
                          setGraphic(null);
                      } else {
                          setGraphic(new Label(format.format(item)));
                      }
                  }
              };
          }
      }
      

      使用示例

      birthday.setCellFactory(new ColumnFormatter<Person, Date>(new SimpleDateFormat("dd MMM YYYY")));
      amplitude.setCellFactory(new ColumnFormatter<Levels, Double>(new DecimalFormat("0.0dB")));
      

      【讨论】:

      • 但是使用的是哪个属性?您的示例中没有属性名称。
      • @MaxiWu 除了上面的 setCellFactory(),你仍然需要调用 setCellValueFactory() 来绑定一个实际的属性
      【解决方案4】:

      我最近需要这样做 -

      dateAddedColumn.setCellValueFactory(
         new Callback<TableColumn.CellDataFeatures<Film, String>, ObservableValue<String>>() {
            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<Film, String> film) {
               SimpleStringProperty property = new SimpleStringProperty();
               DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
               property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
               return property;
            }
         });
      

      但是 - 在 Java 8 中使用 Lamba 表达式会更容易很多

      dateAddedColumn.setCellValueFactory(
         film -> {
            SimpleStringProperty property = new SimpleStringProperty();
            DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
            return property;
         });
      

      快点使用 Java 8 版本的 oracle!

      【讨论】:

      • 这适用于最初填充表格...如果模型中的日期属性更改晚于 SimpleStringProperty 不发出通知,并且表格未更新。
      【解决方案5】:

      您可以通过细胞工厂实现这一目标。见
      https://stackoverflow.com/a/10149050/682495
      https://stackoverflow.com/a/10700642/682495
      虽然第二个链接是关于ListCell,但同样的逻辑也完全适用于TableCells。

      附:如果您需要一些示例代码,请在此处附上。

      【讨论】:

        【解决方案6】:

        一个通用的解决方案可以这么简单:

        import javafx.scene.control.TableCell;
        import javafx.scene.control.TableColumn;
        import javafx.util.Callback;
        
        public interface AbstractConvertCellFactory<E, T> extends Callback<TableColumn<E, T>, TableCell<E, T>> {
        
            @Override
            default TableCell<E, T> call(TableColumn<E, T> param) {
                return new TableCell<E, T>() {
                    @Override
                    protected void updateItem(T item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item == null || empty) {
                            setText(null);
                        } else {
                            setText(convert(item));
                        }
                    }
                };
            }
        
            String convert(T value);        
        }
        

        及其示例用法:

        TableColumn<Person, Timestamp> dateCol = new TableColumn<>("employment date");
        dateCol.setCellValueFactory(new PropertyValueFactory<>("emploumentDateTime"));    
        dateCol.setCellFactory((AbstractConvertCellFactory<Person, Timestamp>) value -> new SimpleDateFormat("dd-MM-yyyy").format(value));
        

        【讨论】:

          【解决方案7】:

          这就是我所做的,而且我工作得很好。

          tbColDataMovt.setCellFactory((TableColumn<Auditoria, Timestamp> column) -> {
              return new TableCell<Auditoria, Timestamp>() {
                  @Override
                  protected void updateItem(Timestamp item, boolean empty) {
                      super.updateItem(item, empty);
                      if (item == null || empty) {
                          setText(null);
                      } else {
                          setText(item.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
                      }
                  }
              };
          });
          

          【讨论】:

            【解决方案8】:

            您可以轻松地通过管道传输不同类型的属性,并在两者之间放置格式化程序或转换器。

                //from my model
                ObjectProperty<Date> valutaProperty;
            
                //from my view
                TableColumn<Posting, String> valutaColumn;
            
                valutaColumn.setCellValueFactory(
                        cellData -> {
                              SimpleStringProperty property = new SimpleStringProperty();
                              property.bindBidirectional(cellData.getValue().valutaProperty,  new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN));
                              return property;
                           });
            

            【讨论】:

              【解决方案9】:

              StringConverter 类是另一种机制。

              TextFieldTableCell 的构造函数如下:public TextFieldTableCell(StringConverter&lt;T&gt; converter)

              ... 和StringConverters 由诸如LocalDateStringConverter 之类的子类组成。然后,默认实现将是:

              new TextFieldTableCell( new LocalDateStringConverter() );
              

              ...这还不错,但无参数LocalDateStringConverter 使用格式为“dd/mm/yyyy”的日期进行解析(fromString() 方法)和toString()。但是还有其他构造函数可以传递FormatStyleDateTimeFormatter

              然而,从我的实验来看,StringConverters 有一点问题,因为很难捕获 fromString() 抛出的带有无效日期的 DateTimeParseException

              这可以通过创建自己的StringConverter 类来解决,例如:

              class ValidatingLocalDateStringConverter extends LocalDateStringConverter {
                  boolean valid;
              
                  @Override
                  LocalDate fromString(String value) {
                      valid = true;
                      if (value.isBlank()) return null;
                      try {
                          // NB wants ISO
                          return LocalDate.parse( value );
                      } catch ( DateTimeParseException e) {
                          valid = false;
                      }
                      return null;
                  }
              
                  @Override
                  String toString( LocalDate date ){
                      // NB returns ISO or the String "null" with null date value (!)
                      String s = date.toString();
                      return s.equals( 'null' )? '' : s;
                  }
              }
              

              使用此StringConverter 解决方案将意味着日期按时间顺序排序,而不管String 表示形式如何。

              【讨论】:

                猜你喜欢
                • 2014-09-04
                • 1970-01-01
                • 2012-09-24
                • 2016-05-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-01-09
                • 1970-01-01
                相关资源
                最近更新 更多