创建一个带有标签和复选框的树,检测其父项是否已选中
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建一个带有标签和复选框的树,检测其父项是否已选中相关的知识,希望对你有一定的参考价值。
我想创建一个树视图,其项目有一个标签,左边是一个复选框。我尝试写如下,但当我单击按钮时,它会打印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);
}
}
我不确定这是写这样的最佳方式,或者你可以完全给你自己的代码。我的意图是创建一个带有标签和复选框的树,然后检测其父项是否已被选中
我认为你的代码打印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
的selected
。
CheckBoxTreeItem<?> parent = (CheckBoxTreeItem<?>) item.getParent();
System.out.println(parent.isSelected());
在这种情况下,铸造它没关系,因为我们知道所有TreeItem
s将是CheckBoxTreeItem
的实例。
由于你还想要一个Label
作为TreeCell
的一部分,你可以简单地将CheckBoxTreeItem
的图形设置为带有所需文本的Label
。但请注意,这会将Label
置于实际复选框的右侧。如果,如你的代码所示,你想要Label
在CheckBox
的左边,这会让你更多参与其中。
在内部,CheckBoxTreeCell
采用TreeItem
的图形并将其设置在内部CheckBox
上;然后它将CheckBox
设置为其自身的图形。由于CheckBox
的设计方式,将CheckBox
的图形/文本放在实际复选框的另一侧(带有支票的视觉框)并不简单(可能甚至不可能)。如果你想将TreeItem
s图形放在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());
几点说明:
- 这取决于如何在内部实现
CheckBoxTreeCell
(特别是Java 10,但也可能适用于Java 8)。如果他们将来改变实施,这可能会破裂。 - 这要求您将
Label
设置为TreeItem
s图形,而不是仅在TreeCell
中使用它。如果您不想这样做,请删除使用TreeItem
s图形的行为,并将其替换为内部Label
。这将允许您删除使用graphicListener
和weakGraphicListener
。如果你想显示TreeItem
的图形,那么你也会删除cBox.setGraphic(null)
。 - 我没有尝试缓存
HBox
(如果根据Note#2更改内容,可以缓存Label
)。相反,我每次只创建一个新的HBox
。更改代码应该很简单,因此它会缓存HBox
,但是,如果你愿意的话。
如果你不想使用CheckBoxTreeItem
但宁愿外化是否选择了一个项目,你应该看看这些方法:
如果您仍然需要CheckBox
左侧的图形,您还必须在此处扩展CheckBoxTreeCell
。上面实现CustomCheckBoxTreeCell
的唯一真正区别是你必须提供一种方法来设置你将用于上述两种方法的Callback
到CustomCheckBoxTreeCell
。您可以通过公开一个带有Callback
的构造函数或在单元格工厂中设置selectedStateCallback
属性来完成此操作。
以上是关于创建一个带有标签和复选框的树,检测其父项是否已选中的主要内容,如果未能解决你的问题,请参考以下文章