我想创建一个树状视图,其项目在左侧有一个标签和一个复选框。 我尝试如下编写,但是当我单击该按钮时,它将打印为null。如果可以使图形不为null,则可以在其中获得复选框。 我的目的是知道父项的复选框是否已选中。
package com.qy.tth.fxgui;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TreeCheck extends Application{
public static void main(String[] args) {
launch(null);
}
private TreeItem<String> item11;
@Override
public void start(Stage stage) throws Exception {
TreeItem<String> item1=new TreeItem<>("1");
TreeItem<String> item2=new TreeItem<>("2");
item11=new TreeItem<>("1-1");
TreeItem<String> itemRoot=new TreeItem<>("root");
item1.getChildren().add(item11);
itemRoot.getChildren().addAll(item1,item2);
TreeView<String> tv=new TreeView<>();
tv.setRoot(itemRoot);
tv.setCellFactory(tv1 -> new TreeCell<String>() {
private HBox hb;
{
Label lable = new Label("icon");
CheckBox cb=new CheckBox();
hb=new HBox();
hb.getChildren().addAll(lable,cb);
setGraphic(hb);
}
protected void updateItem(String value, boolean empty) {
super.updateItem(value, empty);
if (empty || value == null) {
setText("");
hb.setVisible(false);
}else{
setText(value);
hb.setVisible(true);
}
};
});
Button btn=new Button("show parent");
btn.setOnAction(e->showParent());
VBox vb=new VBox();
vb.getChildren().addAll(btn,tv);
Scene scene=new Scene(vb);
stage.setScene(scene);
stage.show();
}
private void showParent() {
TreeItem<String> item1 = item11.getParent();
Node graph = item1.getGraphic();
System.out.println(graph);
}
}
我不确定这是最好的编写方式,或者您可以完全给自己编写代码。 我的意图是创建一个带有标签和复选框的树,然后检测其父级是否被选中
答案 0 :(得分:0)
我相信您的代码正在打印null
的原因是TreeItem
上没有设置图形。在HBox
上设置图形(TreeCell
),然后稍后尝试从TreeItem
检索该图形。
话虽如此,如果您对Label
的右处的CheckBox
感到满意,则无需创建自己的TreeCell
实现。相反,您可以为此使用内置类:CheckBoxTreeCell
和CheckBoxTreeItem
。
要设置TreeView
,请使用以下代码:
TreeView<String> tree = new TreeView<>();
tree.setCellFactory(CheckBoxTreeCell.forTreeView());
静态方法CheckBoxTreeCell.forTreeView()
假定根TreeItem
和所有后代都是CheckBoxTreeItem
的实例。默认情况下,CheckBoxTreeCell
的行为是:
如果您不希望出现这种情况,则应将independent
属性设置为true
。该属性指出:
用于表示此对象的独立状态的BooleanProperty CheckBoxTreeItem。独立状态用于表示是否 对单个CheckBoxTreeItem的更改应影响其状态 父母和孩子。
默认情况下,独立属性为false,这意味着当 CheckBoxTreeItem的状态更改为选定状态或不确定状态 属性,相关CheckBoxTreeItems的状态可能是 改变了。如果独立属性设置为true,则状态为 相关的CheckBoxTreeItems将永远不会改变。
通过这种方式设置TreeView
可以轻松确定是否选择了父级,因为CheckBoxTreeItem
具有名为BooleanProperty
的{{1}}。
selected
在这种情况下,可以进行强制转换,因为我们知道所有CheckBoxTreeItem<?> parent = (CheckBoxTreeItem<?>) item.getParent();
System.out.println(parent.isSelected());
都是TreeItem
的实例。
由于您还希望将CheckBoxTreeItem
作为Label
的一部分,因此可以简单地将TreeCell
的图形设置为带有所需文本的CheckBoxTreeItem
。 但是请注意,这会将Label
置于实际复选框的 right 处。如代码中所示,如果您希望Label
到Label
的左处。
在内部,CheckBox
拍摄CheckBoxTreeCell
的图形并将其设置在内部TreeItem
上;然后将CheckBox
设置为其自身的图形。由于CheckBox
的设计方式,将CheckBox
的图形/文字放在实际复选框(带有复选框的可视框的另一面)上并不简单(甚至不可能) )。如果要将CheckBox
的图形放在TreeItem
的左侧,则必须使用类似CheckBox
的图形(就像在代码中一样)。如果直接扩展HBox
而不是直接扩展CheckBoxTreeCell
,则比尝试简单得多。
TreeCell
然后您将像这样设置单元工厂:
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.HBox;
import javafx.util.Callback;
public class CustomCheckBoxTreeCell<T> extends CheckBoxTreeCell<T> {
public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView() {
return treeView -> new CustomCheckBoxTreeCell<>();
}
private final InvalidationListener graphicListener = (obs) -> updateItem(getItem(), isEmpty());
private final WeakInvalidationListener weakGraphicListener = new WeakInvalidationListener(graphicListener);
public CustomCheckBoxTreeCell() {
// This again assumes that all TreeItems will actually be
// instances of CheckBoxTreeItem
super(item -> ((CheckBoxTreeItem<?>) item).selectedProperty());
treeItemProperty().addListener((observable, oldItem, newItem) -> {
if (oldItem != null) {
oldItem.graphicProperty().removeListener(weakGraphicListener);
}
if (newItem != null) {
newItem.graphicProperty().addListener(weakGraphicListener);
}
});
}
@Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (!empty && getTreeItem().getGraphic() != null) {
CheckBox cBox = (CheckBox) getGraphic();
cBox.setGraphic(null);
HBox hBox = new HBox(getTreeItem().getGraphic(), cBox);
hBox.setAlignment(Pos.CENTER_LEFT);
setGraphic(hBox);
}
}
}
一些注意事项:
这取决于内部如何实现tree.setCellFactory(CustomCheckBoxTreeItem.forTreeView());
(特别是Java 10,但也可能与Java 8一起使用)。如果他们将来更改实施方式,则可能会失败。
这要求您将CheckBoxTreeCell
设置为Label
的图形,而不是仅在TreeItem
中使用它。如果您不想执行此操作,请删除使用TreeCell
图形的行为,并将其替换为内部TreeItem
。这将允许您删除对Label
和graphicListener
的使用。如果您还想显示weakGraphicListener
的图形,则还可以删除TreeItem
。
我不尝试缓存cBox.setGraphic(null)
(或者如果您基于注释#2进行更改,则可能尝试HBox
)。相反,我每次都只创建一个新的Label
。更改代码应该很简单,因此它可以缓存HBox
,但是,如果您选择。
如果您不想使用HBox
,而是想外部化是否选择了某个项目,则应查看以下方法:
如果您仍然希望将图形放在CheckBoxTreeItem
的左上,则也必须在此处扩展CheckBox
。上面的CheckBoxTreeCell
的实现方式的唯一真正区别是,您必须提供一种将将以上两种方法使用的CustomCheckBoxTreeCell
设置为Callback
的方法。您可以通过公开采用CustomCheckBoxTreeCell
的构造函数或通过在单元工厂中设置selectedStateCallback
属性来实现此目的。