2015-03-05

CSV ファイルを読み込んで JavaFX の TableView に表示する

JavaFX に、一時期は大変注目していたのですが、TableView に読み込んだ CSV ファイルの内容を読み込んで表示することに挫折して以来、しばらく遠ざかってしまっていました。

Java 8 のリリースから、JavaFX 2 もバージョン番号を合わせ、JavaFX 8 になりました。あらためて JavaFX のプログラミングに取り組もうと、いろいろチュートリアルを見ましたが、TableView については、相変わらず列のサイズ(仕様)が決まっているデータを扱うサンプルばかりで、予め大きさが判らない、任意のファイルを読み込むような場合にどうするかについて答えてくれるようなサンプルに出会えませんでした。

それでは、自分でやり方を見つけ出せば良いのでしょうが、インターネットでなんでも情報を探し出せるようになると、人間、楽をしたがるものです。自分と同じことをしようとしている人が必ずいるはずだと信じて、丁寧に探し続けたところ、ついに見つけました【参考サイト 1.】

仕組みを判りやすく人様に説明できるほど咀嚼できていません。でも、とにかく動作した、というサンプルを紹介してしまいます。自分が GUI のアプリケーションを開発する場合、このようなスプレッドシート状のウィジェットを使いこなすことは避けては通れませんので、十分に理解できた上で改めて TableView の使い方を紹介します。今回は、まだ中途半端ですが、CSV ファイルを読み込んで TableView に表示する、動作確認用のサンプルを下記に示しました。

なお、動作環境は次の通りです。

  • OS: WIndows 8.1 (64bit)
  • Java: Java SE 1.8.0_31-b13
  • IDE: NetBeans IDE 8.0.2
リスト:TableViewSample.java 
package tableviewsample;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TableViewSample extends Application {

    ObservableList<String> headers = FXCollections.observableArrayList();
    ObservableList<ObservableList> data = FXCollections.observableArrayList();

    FileChooser fileChooser = new FileChooser();
    TableView<ObservableList> table;

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("CSV Read Sample");

        MenuBar menuBar = new MenuBar();
        generateMenu(menuBar, stage);

        table = new TableView();

        VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(menuBar, table);
        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    private void generateMenu(MenuBar menuBar, Stage stage) {
        // Menu File
        Menu menuFile = new Menu("File");
        menuBar.getMenus().addAll(menuFile);

        // MenuItem Exit
        MenuItem open = new MenuItem("Open");
        open.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent t) {
                fileChooser.setTitle("Open CSV File");
                fileChooser.getExtensionFilters().addAll(
                        new FileChooser.ExtensionFilter("CSV", "*.csv"),
                        new FileChooser.ExtensionFilter("All", "*.*")
                );
                File file = fileChooser.showOpenDialog(stage);
                if (file != null) {
                    readCSV(file);
                    open.setDisable(true);
                }
            }
        });

        // MenuItem Exit
        MenuItem exit = new MenuItem("Exit");
        exit.setOnAction((ActionEvent t) -> {
            System.exit(0);
        });

        menuFile.getItems().addAll(open, new SeparatorMenuItem(), exit);
    }

    void readCSV(File file) {
        try {
            if (checkBeforeReadfile(file)) {
                FileInputStream in = new FileInputStream(file);
                InputStreamReader sr = new InputStreamReader(in, "UTF-8");
                BufferedReader br = new BufferedReader(sr);

                String line;
                boolean header = true;
                while ((line = br.readLine()) != null) {
                    StringTokenizer token = new StringTokenizer(line, ",");
                    ObservableList<String> lineList = FXCollections.observableArrayList();

                    if (header) {
                        while (token.hasMoreTokens()) {
                            headers.add(token.nextToken());
                        }
                        header = false;
                    } else {
                        while (token.hasMoreTokens()) {
                            lineList.add(token.nextToken());
                        }
                        data.add(lineList);
                    }
                }
            } else {
                System.out.println("No file exists or can't open.");
            }
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException : " + e.getMessage());
        } catch (IOException e) {
            System.out.println("IOException : " + e.getMessage());
        }

        generateTable();

    }

    private void generateTable() {
        int colN = 0;
        TableColumn[] column = new TableColumn[headers.size()];
        for (String colName : headers) {
            final int idx = colN;
            column[colN] = new TableColumn(colName);
            column[colN].setCellValueFactory(
                    new Callback<CellDataFeatures<ObservableList, String>, ObservableValue<String>>() {
                        @Override
                        public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param) {
                            return new SimpleStringProperty(param.getValue().get(idx).toString());
                        }
                    });
            colN++;
        }
        table.getColumns().addAll(column);

        table.setItems(data); // finally add data to tableview
    }

    boolean checkBeforeReadfile(File file) {
        if (file.exists()) {
            if (file.isFile() && file.canRead()) {
                return true;
            }
        }
        return false;
    }

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

実行例を以下に示しました。

今後、大きなファイルを読み込むなどベンチマークを重ねて、その結果を紹介するようにします。

参考サイト

  1. Updated: Dynamic TableView Data From Database « Java and FX

 

0 件のコメント: