gpt4 book ai didi

combobox - JavaFX 8.0 TableView 是否存在排序错误?

转载 作者:行者123 更新时间:2023-12-03 22:28:18 27 4
gpt4 key购买 nike

Java 8.0 x64、Win7 x64、Clojure、Emacs。

我在 Clojure 中使用 TableView 做一些事情,其中​​我正在 proxying TableCell 因此我可以在其中呈现和编辑任意内容它。这些值是原子内部映射的字段。代码如下。它利用了大量的实用函数和宏来简化这个过程,但你明白了要点。主要是单元格的图文属性的管理。

有一个键盘处理程序附加到 ComboBox,因此它知道用户何时按下 ENTER 等。此处理程序在单元格散焦时被删除,因此我们最终不会在对象中有多个处理程序。

在此示例中,我有三列,一列用于字段名称(一个仅显示文本且不可编辑的简单单元格工厂),一列用于值(花式单元格工厂),一列用于类型(简单细胞工厂)。使用一些示例数据的输出如下所示:

enter image description here

当我根据 Value 对表格进行排序时,事情似乎工作正常,如下所示:

通常,当键盘处理程序触发时,它会调用单元格的 commitEdit 函数,该函数会调用其 TableCell 父类(super class) commitEdit。然后,幕后的 TableView 魔术调用列的 onEditCommit 处理程序,该处理程序实际上将编辑提交到数据库。父类(super class) commitEdit 返回后,单元格的 commitEdit 就没有什么可做的了。单元格的 updateItem 然后由 TableView 自动调用,用单元格的正常内容替换 ComboBox

问题

当我根据 Field 列对表格进行排序一次或多次,或者根据 Type 列对表格进行排序两次或更多次 并尝试使用 ComboBox(在本例中为颜色选择器)编辑内容,需要额外单击才能使 ComboBox 下拉,然后按 ENTER 按键不起作用,具体如下:

原因

在损坏的情况下,TableCell 的父类(super class)似乎会立即返回并且不会调用列的 onCommitEdit 处理程序,单元格的处理程序也不会updateItem 被调用,因此单元格不会呈现回其正常的非编辑状态,即没有 ComboBox

正常情况和损坏情况如下所示: enter image description here

此处显示正常情况和损坏情况下的调试文本输出。 enter image description here

奇怪的是这个问题有时也会出现在非颜色的 ComboBox 中(sides 字段有一个 ComboBox 编辑器例如数字)。

那么这是 JavaFX TableView 中的错误吗?还是我做错了什么?

(defn add-handlers!
"Adds common keyboard handler and focus listener to temporary editing graphic.
graphic is typically textfield or combo-box. cell is tablecell which
is being edited. getterfn is function to get value from graphic so
it can be commited to database."
[graphic cell getterfn]
(let [focus-listener (make-focus-change-listener cell getterfn)]
(println "adding focus and keyboard listener")
(add-listener! graphic :focused focus-listener)
(.setOnKeyPressed graphic (eventhandler [e] ;; here "cell" still refers to the tablecell
(condp = (.getCode e)
KeyCode/ENTER (do (println "ENTER pressed. Removing focus listener")
(remove-listener! graphic :focused focus-listener) ;; Prevent double-commit on defocus
(.commitEdit cell (getterfn)))
KeyCode/ESCAPE (do (println "ESC pressed. Removing focus listener")
(remove-listener! graphic :focused focus-listener) ;; Prevent double-commit on defocus
(.cancelEdit cell)) ;; Removes textfield
KeyCode/TAB (let [index (.. cell getTableRow getIndex)
next-column (get-next-column cell (not (.isShiftDown e)))]
(println "TAB pressed. Removing focus listener")
(remove-listener! graphic :focused focus-listener) ;; Prevent double-commit on defocus
(.commitEdit cell (getterfn))
(.edit (.getTableView cell) index next-column))
nil))))) ;; do nothing


(defn make-combobox
"Implements dropdown combobox. 'cell' is fancy table cell in
question. 'items' is list of things for dropdown, which can be
anything that the dropdown can render and choose as the final item"
[cell initvalue & [items]]
(let [cmb (jfxnode ComboBox (observable items))
cell-factory FANCY-LISTCELL-FACTORY
blank-cell (.call cell-factory nil)]
(doto cmb
(add-handlers! cell #(.getValue cmb))
(.setValue initvalue)
(.setButtonCell blank-cell)
(.setCellFactory cell-factory))))


(defn render-cell-with-item!
"Puts correct item in cell graphic and/or text property based on item
type. Additional arguments for editing such as drop-down, are
handled in the startEdit function; this function just renders the
cell when called by updateItem or cancelEdit."
[cell item]
(cond
(instance? Node item) (set-graphic-text! cell item nil) ;; for a graphic/Node item
(instance? Boolean item) (let [[var full-accesspath] (calc-full-accesspath cell)
cb (jfxnode CheckBox
:text (str item)
:selected item
:disable (not (mutable? var)))]
(.setEditable cell false)
(set-graphic-text! cell cb nil)
(when (mutable? var)
(uni-bind! (.selectedProperty cb) var full-accesspath)))
(instance? clojure.lang.PersistentVector item) (set-graphic-text! cell (Label. "Put vector editor here") nil)
(instance? Color item) (set-graphic-text! cell (make-color-box item) (color-map-inverse item))
;; All other types go here, presumably text types, so assume editable
:else (set-graphic-text! cell nil (si/to-normstr item)))) ;; else set underlying text


(def FANCY-TABLECELL-FACTORY
"The main callback interface which constructs the actual each cell
for arbitrary types. Assumes an editable cell for text representations."
(callback [column]
(proxy [TableCell] []
(updateItem [item empty]
(proxy-super updateItem item empty)
(when (not empty)
(render-cell-with-item! this item)))

(startEdit []
(proxy-super startEdit)
;; Change to appropriate graphic when editing
(println "in proxy's startEdit. Column commitHandler is" (.getOnEditCommit column))
(let [item (apply access-db (calc-full-accesspath this))
options (get-field-options this)] ;; could be nil ...
(if-let [combo-items (:combo-items options)] ;; ... so put as argument to :combo-items
(let [cmb (make-combobox this item combo-items)]
(set-graphic-text! this cmb nil)
(.requestFocus cmb)
(.show cmb)) ;; This makes drop-down appear without clicking twice.
(when (textish? item)
(let [tf (make-textfield-editor this)]
(set-graphic-text! this tf nil) ;; just set tf as graphic; leave existing text alone
(.requestFocus tf)
(.selectAll tf))))))
(cancelEdit []
;; CancelEdit gets called either by defocus or by ESC.
;; In any case, use the item currently in the database
;; for this cell and just render as in updateItem
(proxy-super cancelEdit)
(let [item (apply access-db (calc-full-accesspath this))]
(render-cell-with-item! this item)))
(commitEdit [value]
;; Nothing to do here. All commits happen either in the textField callback or in the column edit callback
(println "in cell's commitEdit, before super")
(proxy-super commitEdit value)
(println "in cell's commitEdit, after super")))))


(defn inner-table-view*
"Make inner table view for use by inspector-view and table-view"
[var accesspath columns]
(let [obslist (observable (var-snapshot var accesspath))]
(jfxnode TableView
:user-data {:var var ;; the actual var...
:accesspath accesspath } ;; ... and how to get to the displayed data
:items obslist
:columns columns
:editable (mutable? var))))

(defn inspector-view
"Takes plain map or atom/var/ref/agent of map and displays fields
and values in JFX TableView. Compound values (ie maps, vectors,
etc., for now are just displayed as their string value. If access
is supplied, assumes m is var/ref/atom and assigns appropriate
linkage between m and view contents. The topmost available var or
map is assigned to the TableView, and the accessor for each field is
assigned to each column."
[var & {:keys [accesspath field-options]}]
(let [ismutable (mutable? var)
field-col (jfxnode TableColumn "Field"
:cell-value-factory CELL-VALUE-FACTORY
:cell-factory SIMPLE-TABLECELL-FACTORY
:user-data {:accessfn key } ;; label-only option not relevant yet
:editable false
:sortable true)
value-col (jfxnode TableColumn "Value"
:cell-value-factory CELL-VALUE-FACTORY
:cell-factory FANCY-TABLECELL-FACTORY
:user-data {:accessfn val} ;; val is fn for accessing cell values from data item
:on-edit-start (eventhandler [e] (println "editing column " (.getOldValue e) (.getNewValue e)))
:on-edit-cancel (eventhandler [e] (println "canceling column with event" e))
:on-edit-commit (eventhandler [e] (do (println "column's on-edit-commit handler calling column-commit") (column-commit e)))
:editable ismutable
:comparator columnComparator)
type-col (jfxnode TableColumn "Type"
:cell-value-factory CELL-VALUE-FACTORY
:cell-factory SIMPLE-TABLECELL-FACTORY
:user-data {:accessfn #(type (val %))}
:editable false
:sortable true)
cols [field-col value-col type-col]

tv (inner-table-view* var accesspath cols)]
;; Add options to table's userData. This is for inspector-view
;; not table-view, so we don't put this in inner-table-view
;; function
(let [userdata (.getUserData tv)
newuserdata (conj userdata {:field-options field-options})]
(.setUserData tv newuserdata))

;; Add watches, use tv instance as key so we can remove it later
;; This gets called each time db is changed.
(if (mutable? var)
(add-watch var tv (fn [k r o n] ;; Key Ref Old New
(println "Inside KRON with new var" n)
;; Capture the existing sort order and type
;; Taken from http://stackoverflow.com/questions/11096353/javafx-re-sorting-a-column-in-a-tableview
(let [sort-order (vec (.getSortOrder tv)) ;; need to remember ObservableList<TableColumn> and vectorize or it gets reset from underneath us
sort-types (map #(.getSortType %) sort-order)
sortables (map #(.isSortable %) sort-order)]

;; Here we actually put the items into the tableview after the change
(.setItems tv (observable (var-snapshot var accesspath)))

;; Sort order is now empty up so we put back what was in it
(let [new-sort-order (.getSortOrder tv)] ;; get ObservableList<TableColumn>
(.setAll new-sort-order (into-array sort-order)) ;; reset the sort order based on what was there before

;; Assign sorting to each column
(doseq [col sort-order, sort-type sort-types, sortable sortables]
(.setSortType col sort-type)
(.setSortable col sortable)))))))
tv))

最佳答案

我发现了问题,这当然是在我的代码中。

因为 JFX 重用单元格,所以即使在单元格中呈现不同的内容时,单元格的 editable 属性仍然存在。在我的例子中,我的数据库中有一个 bool 值成员,我将其呈现为一个复选框。复选框本身是可点击的,但呈现它的单元格不可编辑。当此单元格在使用不同项目排序后重新呈现时,非编辑状态持续存在并搞砸了新项目的编辑,这不知何故导致下拉框无法正常消失。实际上这个错误也出现在非组合框项目中,例如文本编辑等。

因此解决方案是为呈现的每个项目类型显式设置单元格的可编辑属性。

关于combobox - JavaFX 8.0 TableView 是否存在排序错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31888184/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com