JavaFX表格格式

时间:2012-07-10 11:27:49

标签: format render cell javafx-2

    TableColumn<Event,Date> releaseTime  = new TableColumn<>("Release Time");
    releaseTime.setCellValueFactory(
                new PropertyValueFactory<Event,Date>("releaseTime")
            );

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

9 个答案:

答案 0 :(得分:9)

如果要保留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,我花了一段时间才弄明白这一点;所以希望这会为其他人节省一些时间!

答案 1 :(得分:7)

我建议使用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")));

答案 2 :(得分:6)

我最近需要这样做 -

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

然而 - 使用Lamba表达式在Java 8中使用 lot 更容易:

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!

答案 3 :(得分:4)

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

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

答案 4 :(得分:4)

Java FX8更新:

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

与之前的答案有些不同: 我将日期类型保留在列上,因此我需要同时使用cellValueFactory和cellFactory。 我创建一个通用的可重用方法来为所有日期列生成cellFactory。 我使用java 8 date for java.time包!但是这个方法可以很容易地重新实现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时间类型(本地区域等)的util函数。

答案 5 :(得分:2)

这就是我所做的,我的工作非常完美。

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

答案 6 :(得分:2)

通用解决方案可以简单:

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 :(得分:0)

您可以轻松管道不同类型的属性,并在其间放置格式器或转换器。

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

答案 8 :(得分:0)

StringConverter类是另一种机制。

TextFieldTableCell具有如下构造函数:public TextFieldTableCell(StringConverter<T> converter)

...和StringConverter由子类组成,例如LocalDateStringConverter。默认的实现将是:

new TextFieldTableCell( new LocalDateStringConverter() );

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

但是,根据我的实验,StringConverter有点问题,因为很难抓住DateTimeParseException抛出的fromString()并带有无效的日期。

这可以通过创建自己的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的表示形式。