ページ

2013年7月16日火曜日

JavaFX - TableView : AutoTableColumnGeneration (1)

TableViewでは、表示したい列を静的に定義して、表示するモデルのコレクションを指定して内容を表示しますが、今回は、表示したい列をモデルから動的に生成するユーティリティクラスを考えてみたいと思います。

このユーティリティクラスは、以下のようにスタティックプロパティでTableViewに指定すると、TableViewのitemsプロパティに値が設定されたときに、設定されたモデルの内容から、TableColumnを動的に作成して、TableViewに追加するようにします。

<TableView AutoTableColumnGeneration.enable="true"/>

まずは、クラス名をAutoTableColumnGenerationとして作成し、スタティックプロパティを定義します。

public final class AutoTableColumnGeneration {
    private static final String AUTO_TABLE_COLUMN_GENERATION_ENABLE = "auto-table-column-generation-enable";

    private static final ChangeListener<? super ObservableList> TABLE_VIEW_ITEMS_CHANGE_LISTENER = new ChangeListener<ObservableList>() {
        @Override
        public void changed(ObservableValue<? extends ObservableList> observableValue, ObservableList oldItems, ObservableList newItems) {
            AutoTableColumnGeneration.generateTableColumns((TableView)((ReadOnlyProperty)observableValue).getBean(), newItems);
        }
    };

    public static <S> boolean isEnable(TableView<S> tableView) {
        Objects.requireNonNull(tableView);

        if (!tableView.hasProperties()) { return false; }

        Object enable = tableView.getProperties().get(AUTO_TABLE_COLUMN_GENERATION_ENABLE);
        return enable != null && (boolean)enable;
    }

    public static <S> void setEnable(TableView<S> tableView, boolean enable) {
        Objects.requireNonNull(tableView);

        tableView.getProperties().put(AUTO_TABLE_COLUMN_GENERATION_ENABLE, enable);
        if (enable) {
            tableView.itemsProperty().addListener(TABLE_VIEW_ITEMS_CHANGE_LISTENER);
        } else {
            tableView.itemsProperty().removeListener(TABLE_VIEW_ITEMS_CHANGE_LISTENER);
        }
    }

    private static void generateTableColumns(TableView tableView, ObservableList items) {
        tableView.getColumns().clear();
        if (items == null || items.isEmpty()) { return; }

        ...

    }
}

TableViewのitemsプロパティに値が設定されたときに、TableColumnを生成するようにします。TableColumnの生成は、指定されたモデルに定義されているReadOnlyPropertyから生成するようにします。そこで、戻り値がReadOnlyPropertyに代入することができるメソッドをリフレクションで取得し、TableColumnのインスタンスを生成し、TableViewにそのインスタンスを追加するようにします。このとき、TableViewのeditableプロパティがtrueの場合に各項目が編集できるように、cellFactoryを設定しておきます。cellFactoryの設定は、取得したReadOnlyPropertyの種類によって、以下のようにします。

Property Cell Converter
StringProperty TextFieldTableCell
BooleanProperty CheckBoxTableCell
IntegerProperty TextFieldTableCell IntegerStringConverter
LongProperty TextFieldTableCell LongStringConverter
FloatProperty TextFiledTableCell FloatStringConverter
DoubleProperty TextFiledTableCell DoubleStringConverter

また、ObjectPropertyで、オブジェクトの型がDateの場合は、TextFieldTableCellをDateTimeStringConverterで、日付の書式を「yyyy/MM/dd HH:mm:ss」として生成するようにします。

上記以外の場合でも、cellFactoryを指定できるように、以下のメソッドを用意しておきます。

public static <S, T> void registerCellFactory(Class<T> cellValueClass, Callback<TableColumn<S, T>, TableCell<S, T>> cellFactory);

セルに設定する値の型をキーにして、cellFactoryを設定するようにします。

以上で、TableColumnを動的に作成することができますが、表示する列の順番は未定となり、また、各TableColumnの詳細の設定ができませんので、これらの設定ができるように、以下のアノテーションを定義しておきます。

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoTableColumn {
    String id() default "";
    String name() default "";
    int order() default Integer.MAX_VALUE;
    boolean editable() default true;
    double prefWidth() default 80;
    double maxWidth() default 5000;
    double minWidth() default 10;
    boolean resizable() default true;
    boolean sortable() default true;
    TableColumn.SortType sortType() default TableColumn.SortType.ASCENDING;
    String[] styleClass() default "";
    String[] cellStyleClass() default "";
}

TableColumnに設定することができるプロパティの値をこのアノテーションで指定できるようにします。AutoTableColumnGenerationでは、このアノテーションが指定されている場合は、設定されている値をTableColumnのプロパティにそれぞれ設定するようにします。ただし、orderについては、TableColumnの表示順を指定し、値の小さい順に表示するようにし、cellStyleClassについては、各セルに指定されているStyleClassを設定するようにします。

今回は、TableViewのTableColumnを動的に作成するユーティリティクラスについて考えてみました。次回は、このクラスを実際に使ってみたいと思います。