日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

JavaFX 遊び TableView 編(2)

どうやら Map データも入れ込めるとの事で試してみる。
Using JavaFX UI Controls: Table View | JavaFX 2 Tutorials and Documentation

上記 URL の Example 12-12 に相当。
途中の追加や編集をすっ飛ばしてるけどこっちに興味を持ったのでしょうがないw

解説面倒なんでソースだけ載っけます。

project.clj

(defproject fx-map-table "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [local.oracle/javafxrt "2.2.25"]]
  :aot [fx-map-table.core]
  :main fx-map-table.core)

core.clj

(ns fx-map-table.core
  (:import [javafx.application Application Platform]
           [javafx.scene Group Scene]
           [javafx.stage Stage]
           [javafx.collections FXCollections ObservableList]
           [javafx.geometry Insets]
           [javafx.scene.control Label TableCell TableColumn TableView TextField]
           [javafx.scene.control.cell MapValueFactory TextFieldTableCell]
           [javafx.scene.layout VBox]
           [javafx.scene.text Font]
           [javafx.util Callback StringConverter])
  (:gen-class
   :extends javafx.application.Application))

(def ^:private key1 "A")
(def ^:private key2 "B")

(defn make-cell-factory []
  (proxy [Callback] []
    (call [p]
      (TextFieldTableCell.
       (proxy [StringConverter] []
         (toString [t] (.toString t))
         (fromString [string] string))))))

(defn -start [this ^Stage stage]
  (let [scene (Scene. (Group.))
        table (TableView.
               (FXCollections/observableArrayList
                (map #(apply assoc {} %) (for [i (range 1 11)] 
					   [key1 (str key1 i) key2 (str key2 i)]))))
        cell-factory (make-cell-factory)
        vbox (VBox.)
        firstDataColumn (TableColumn. "Class A")
        secondDataColumn (TableColumn. "Class B")
        label (Label. "Student IDs")]
    (doto stage
      (.setTitle "Table View Sample")
      (.setWidth 300)
      (.setHeight 500))
    (.setFont label (Font. "Arial" 20))
    (doto firstDataColumn
      (.setMinWidth 130)
      (.setCellValueFactory (MapValueFactory. key1)))
    (doto secondDataColumn
      (.setMinWidth 130)
      (.setCellValueFactory (MapValueFactory. key2)))
    (.setEditable table true)
    (.. table getSelectionModel (setCellSelectionEnabled true))
    (.. table getColumns (setAll (to-array [firstDataColumn secondDataColumn]))) 
    (.setCellFactory firstDataColumn cell-factory)
    (.setCellFactory secondDataColumn cell-factory)
    (doto vbox
      (.setSpacing 5)
      (.setPadding (Insets. 10 0 0 10)))
    (.. vbox getChildren (addAll (to-array [label table])))
    (as-> (.getRoot scene) obj (cast Group obj) (.getChildren obj) (.addAll obj (to-array [vbox])))
    (doto stage
      (.setScene scene)
      (.show))))

(defn -main [& args]
  (Application/launch fx_map_table.core args))

簡単な違いをちょろっと。

  • サンプルで固定データ生成する generateDataInMap はめんどくさくて TableView を生成する let の所に直接入れた。
  • そろそろ project 名は何となく lisp ライクなネーミングに変更。注意点として Application/launch の部分で指定するクラス名は Java 世界だとアンダーバーに変換されるのでそうしておいた。

まとめ

Clojure の Map データの親和性は高いみたいで、最終的に TableColumns に渡す前までは Clojureで好きにできそうな雰囲気。あと個人的にはサンプルとは言え proxy をまともに使った気がするのもメリットかな。