创建一个带有标签和复选框的树,检测其父项是否已选中

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中检索该图形。

话虽这么说,如果你对LabelCheckBox右侧没有问题,那就没有必要创建自己的TreeCell实现了。相反,您可以使用内置类:CheckBoxTreeCellCheckBoxTreeItem

要设置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有一个名为BooleanPropertyselected

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

在这种情况下,铸造它没关系,因为我们知道所有TreeItems将是CheckBoxTreeItem的实例。


由于你还想要一个Label作为TreeCell的一部分,你可以简单地将CheckBoxTreeItem的图形设置为带有所需文本的Label。但请注意,这会将Label置于实际复选框的右侧。如果,如你的代码所示,你想要LabelCheckBox的左边,这会让你更多参与其中。

在内部,CheckBoxTreeCell采用TreeItem的图形并将其设置在内部CheckBox上;然后它将CheckBox设置为其自身的图形。由于CheckBox的设计方式,将CheckBox的图形/文本放在实际复选框的另一侧(带有支票的视觉框)并不简单(可能甚至不可能)。如果你想将TreeItems图形放在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设置为TreeItems图形,而不是仅在TreeCell中使用它。如果您不想这样做,请删除使用TreeItems图形的行为,并将其替换为内部Label。这将允许您删除使用graphicListenerweakGraphicListener。如果你想显示TreeItem的图形,那么你也会删除cBox.setGraphic(null)
  3. 我没有尝试缓存HBox(如果根据Note#2更改内容,可以缓存Label)。相反,我每次只创建一个新的HBox。更改代码应该很简单,因此它会缓存HBox,但是,如果你愿意的话。

如果你不想使用CheckBoxTreeItem但宁愿外化是否选择了一个项目,你应该看看这些方法:

如果您仍然需要CheckBox左侧的图形,您还必须在此处扩展CheckBoxTreeCell。上面实现CustomCheckBoxTreeCell的唯一真正区别是你必须提供一种方法来设置你将用于上述两种方法的CallbackCustomCheckBoxTreeCell。您可以通过公开一个带有Callback的构造函数或在单​​元格工厂中设置selectedStateCallback属性来完成此操作。

以上是关于创建一个带有标签和复选框的树,检测其父项是否已选中的主要内容,如果未能解决你的问题,请参考以下文章

MFC 菜单项复选框行为

UIButton 标签检测点击特定文本

如何使用 mvc 使用 JSON 数据创建带有复选框的树?

检测是不是未选中任何复选框以更新复选框列表

检测在 Angular.js ng-change 事件中复选框是选中还是未选中

combotree(组合树)的使用