我在JavaFX 2.0上使用Clojure来创建一个包含给定TableColumn
中任意节点的TableView。我已经proxy
使用了一个TableCell,并且在UpdateItem
方法中我尝试了.setGraphic
我要使用的新“真实”节点的各种尝试。我可以创建另一个TableCell
,例如CheckBoxTableCell
,但是在.setGraphic
的内部调用期间,当NullPointerException
调用setPrefWidth
时,该程序崩溃了,基本上当专栏试图摆脱它时。似乎我可以然后放置一个非TableCell
派生节点,例如一个普通按钮,它有自己的ActionEvent
等。我这样做没有问题。
问题:
为什么TableCell
的实例在用作另一个TableCell
的图形时会崩溃?我猜它希望TableColumn
或TableView
成为父母或其他什么。
在下面的缩写代码中,item
是底层数据项,它是一个clojure映射,其值为java bean / properties。
(defmacro callback
"Reifies the callback interface."
[args & body]
`(reify javafx.util.Callback
(~'call [this# ~@args]
~@body)))
(defmacro eventhandler
"Reifies the eventhandler interface."
[args & body]
`(reify javafx.event.EventHandler
(~'handle [this# ~@args]
~@body)))
(defn button-cell-factory
"Uses a proxy of TableCell because there is no ButtonTableCell in javafx."
[]
(callback [callback-column]
(proxy [TableCell] []
(updateItem [item empty]
;; Item is the content of the TableCell, not very interesting since it's just the button and button parameters
;; This is the actual TableCell with all the info we are interested in
(proxy-super updateItem item empty)
(println "in button-cell proxy updateItem" item empty)
(if empty nil
(let [button (doto (Button. (:name item))
(.setMaxWidth Double/MAX_VALUE)
(.setAlignment Pos/CENTER_LEFT)
(.setOnAction
(eventhandler [evt]
(if-let [action (:action item)]
(let [index (.getIndex this)
tableview (.getTableView this)
rowdata (.. tableview (getItems) (get index))]
(action rowdata)) nil))))
imageview (ImageView. (:image item))]
(if (not (nil? imageview)) (.setGraphic button imageview))
(.setGraphic this button)))))))
(defn generic-cell-factory [spec]
"Provide arbitrary cell based on data"
(callback [callback-column]
(proxy [TableCell] []
(updateItem [item empty]
(proxy-super updateItem item empty)
(or empty
(let [inner-cell (.call (button-cell-factory) callback-column)]
(.setGraphic this inner-cell))))))) ;; this crashes when inner-cell is a proxy of TableCell, but not when it's a Button or other non-TableCell node.
(defn make-cell-factory
"Generic factory maker. Dispatches on model, and uses other column specs.
Column in the actual column created. spec is the full spec map. The other names
are the values of the map destructured."
[column {:keys [model] :as spec} ]
(cond ;; ...
(= model :generic) (generic-cell-factory spec)
;; ...
))
(defn make-tableview
"colspecs is a map of keys and names."
[colspecs]
(let [tableview (TableView.)]
(doseq [{:keys [name key editable minwidth] :as colspec} colspecs]
(let [col (TableColumn. name)]
(doto col
;; ...
(.setCellFactory (make-cell-factory col colspec)))
;; I can call (button-cell-factory) here just fine, but trying to put a button-cell in TableCell crashes.
;; ...
(.. tableview (getColumns) (add col))))
tableview))
缩写错误:
SEVERE: javafx.scene.control.Control loadSkinClass Failed to load skin 'StringProperty [bean: TableView[id=null, styleClass=table-view], name: skinClassName, value: com.sun.javafx.scene.control.skin.TableViewSkin]' for control TableView[id=null, styleClass=table-view]
java.lang.NullPointerException
at com.sun.javafx.scene.control.skin.TableCellSkin.computePrefWidth(TableCellSkin.java:102)
at javafx.scene.Parent.prefWidth(Parent.java:867)
at javafx.scene.layout.Region.prefWidth(Region.java:1368)
at javafx.scene.control.Control.computePrefWidth(Control.java:852)
...