非IT企業に勤める中年サラリーマンのIT日記

非IT企業でしかもITとは全く関係ない部署にいる中年エンジニア。唯一の趣味がプログラミングという”自称”プログラマー。

JavaFXのTableViewで列数を動的に生成する方法

   

以前、JavaFXのTableVeiwの使い方について書きましたが、この方法の問題として列数が固定されてしまうという点です。データクラスとリンクさせるためデータクラスのインスタンス変数で決まってしまうからです。

 

そうではなく、任意のCSVファイルを読み込む場合など、列数や列名がデータの都度可変させるにはどうすれば良いか、というのが今回のテーマです。

広告

完成形

完成形はこちらです。列が3つありますが、これはプログラムの中で動的に生成しているものです。データクラスと関連付けしていません。

 

SceneBuilderでも特に列(TableColmun)を貼り付けてはいません。TableView本体のみです。

 

ソースコード全文

まずはデータクラスのソースコードです。ArrayListでデータリストを保持しますが、この要素数が列の数になります。肝はStringProperty get(int index)メソッドで、これを使って各列とArrayListの要素No.とを対比させます。

import java.util.*;
import javafx.beans.property.*;

public class User {
  private ArrayList<String> list;

  public User(ArrayList<String> list) {
    this.list = list;
  }

  public void add(String data){ list.add(data); }
  public StringProperty get(int index){
    return new SimpleStringProperty(list.get(index));
  }
}
 

 

次に本体のソースコードです。

import javafx.application.Application;
import javafx.fxml.*;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.*;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.collections.*;
import javafx.scene.control.cell.*;
import javafx.scene.control.TableColumn.*;
import java.util.*;
import javafx.util.*;
import javafx.beans.value.*;
import javafx.beans.property.*;

public class TableTest2 extends Application implements Initializable {
  @FXML private TableView table;
  private List<TableColumn> columnList;
  private ObservableList<User> data;

  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(Stage stage) throws Exception{
    VBox root = FXMLLoader.load(getClass().getResource("TableTest2.fxml"));
    stage.setTitle("TableView Test");
    stage.setScene(new Scene(root));
    stage.show();
  }

  @Override
  public void initialize(java.net.URL url, java.util.ResourceBundle bundle) {
    columnList = new ArrayList<TableColumn>();

    TableColumn dateCol1 = new TableColumn("名前");
    columnList.add(dateCol1);
    TableColumn dateCol2 = new TableColumn("住所");
    columnList.add(dateCol2);
    TableColumn dateCol3 = new TableColumn("年齢");
    columnList.add(dateCol3);
    table.getColumns().addAll(columnList);

    dateCol1.setCellValueFactory(new Callback<CellDataFeatures<User, String>, ObservableValue<String>>() {
      public ObservableValue<String> call(CellDataFeatures<User, String> p) {
        return p.getValue().get(0);
      }
    });

    dateCol2.setCellValueFactory(new Callback<CellDataFeatures<User, String>, ObservableValue<String>>() {
      public ObservableValue<String> call(CellDataFeatures<User, String> p) {
        return p.getValue().get(1);
      }
    });

    dateCol3.setCellValueFactory(new Callback<CellDataFeatures<User, String>, ObservableValue<String>>() {
      public ObservableValue<String> call(CellDataFeatures<User, String> p) {
        return p.getValue().get(2);
      }
    });

    data = FXCollections.observableArrayList();
    table.itemsProperty().setValue(data);
    table.setItems(data);

    ArrayList<String> col = new ArrayList<String>();
    col.add("鈴木太郎");
    col.add("東京");
    col.add("34");
    data.addAll( new User(col) );
  }
}
 

 

ポイントは以下の部分です。列数固定の場合はsetCellValueFactoryの引数にデータクラスのインスタンス変数名をあてがっていましたが、今回は以下のようにCallbackクラスを使います。

return p.getValue().get(0);getメソッドは、先ほどのUserクラスのStringProperty get(int index)メソッドです。

dateCol1.setCellValueFactory(new Callback<CellDataFeatures<User, String>, ObservableValue<String>>() {
  public ObservableValue<String> call(CellDataFeatures<User, String> p) {
    return p.getValue().get(0);
  }
});
 

 

以下のJavaの公式ヘルプページの「プロパティを含まないクラスに対するTableViewのサポート」に解説が書かれているので参照してください。

 

今回の方法を使えば、例えばcsvファイルを読み込んで先頭の項目行からTableViewの列を生成して2行目以下のデータを読み込む、という方法が可能になります。

 

広告

 - Java