创建一棵带有标签和复选框的树,检测其父级是否被选中

问题描述 投票:0回答:1

我想创建一个树视图,其项目在左侧有一个标签和一个复选框。 我尝试写如下,但是当我单击按钮时,它打印 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);
    }
}

我不确定这样写是不是最好的方法,或者你可以完全给出自己的代码。 我的意图只是创建一棵带有标签和复选框的树,然后检测其父级是否被选中

javafx tree treeview
1个回答
0
投票

我相信您的代码打印

null
的原因是因为
TreeItem
上没有设置图形。您在
HBox
上设置图形(
TreeCell
),然后尝试从
TreeItem
检索该图形。

话虽这么说,如果您同意

Label
位于 CheckBox
right
,则无需创建自己的
TreeCell
实现。相反,您可以使用内置类来实现此目的:
CheckBoxTreeCell
CheckBoxTreeItem

要设置

TreeView
,您可以使用以下代码:

TreeView<String> tree = new TreeView<>();
tree.setCellFactory(CheckBoxTreeCell.forTreeView());

静态方法

CheckBoxTreeCell.forTreeView()
假设根
TreeItem
和所有后代都是
CheckBoxTreeItem
的实例。默认情况下,
CheckBoxTreeCell
的行为是:

  • 如果没有选择子项,则不会选择父项。
  • 如果选择了部分(但不是全部)子级,则父级是不确定的。
  • 如果选择了所有子项,则选择了父项。

如果您不希望出现此行为,则应将

independent
属性设置为
true
。该属性声明:

用于表示此独立状态的布尔属性 CheckBoxTreeItem。独立状态用于表示是否 对单个 CheckBoxTreeItem 的更改应该影响其状态 家长和孩子。

默认情况下,独立属性为 false,这意味着当 CheckBoxTreeItem 的状态更改为选定或不确定 属性,相关 CheckBoxTreeItems 的状态可能是 改变了。如果独立属性设置为 true,则状态 相关的 CheckBoxTreeItems 永远不会改变。


以这种方式设置

TreeView
可以轻松确定是否选择了父级,因为
CheckBoxTreeItem
有一个名为
BooleanProperty
selected

CheckBoxTreeItem<?> parent = (CheckBoxTreeItem<?>) item.getParent();
System.out.println(parent.isSelected());

在这种情况下,可以进行强制转换,因为我们知道所有

TreeItem
都将是
CheckBoxTreeItem
的实例。


由于您还希望将

Label
作为
TreeCell
的一部分,您只需将
CheckBoxTreeItem
的图形设置为带有所需文本的
Label
即可。 但请注意,这会将
Label
置于实际复选框的right 处。如果,如代码中所示,您希望将
Label
置于 CheckBox
left
上,这会涉及更多一点

在内部,

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());

一些注意事项:

  1. 这取决于

    CheckBoxTreeCell
    的内部实现方式(特别是 Java 10,但也可能适用于 Java 8)。如果他们将来改变实现,这可能会失败

  2. 这需要您将

    Label
    设置为
    TreeItem
    的图形,而不是仅在
    TreeCell
    中使用它。如果您不想这样做,请删除使用
    TreeItem
    图形的行为并将其替换为内部
    Label
    。这将允许您删除
    graphicListener
    weakGraphicListener
    的使用。如果您还想显示
    TreeItem
    的图形,那么您还需要删除
    cBox.setGraphic(null)

  3. 我不会尝试缓存

    HBox
    (或者如果您根据注释 #2 进行更改,则可能会缓存
    Label
    )。相反,我每次都会创建一个新的
    HBox
    。更改代码应该很简单,这样它就可以缓存
    HBox
    ,但是,如果您这样选择


如果您不想使用

CheckBoxTreeItem
但更愿意外部化是否选择了某个项目,则应该查看以下方法:

如果您仍然想要 CheckBox

left
上的图形,您还必须在此处扩展
CheckBoxTreeCell
。与上面如何实现
CustomCheckBoxTreeCell
的唯一真正区别是,您必须提供一种方法来将与上述两种方法一起使用的
Callback
设置为
CustomCheckBoxTreeCell
。您可以通过公开采用
Callback
的构造函数或通过在单元工厂内设置
selectedStateCallback
属性来实现此目的。

© www.soinside.com 2019 - 2024. All rights reserved.