创建带有标签和chekcbox的树,检测其父级是否被chekced

时间:2018-07-12 02:26:36

标签: javafx tree treeview

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

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

1 个答案:

答案 0 :(得分:0)

我相信您的代码正在打印null的原因是TreeItem上没有设置图形。在HBox上设置图形(TreeCell),然后稍后尝试从TreeItem检索该图形。

话虽如此,如果您对Label处的CheckBox感到满意,则无需创建自己的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具有名为BooleanProperty的{​​{1}}。

selected

在这种情况下,可以进行强制转换,因为我们知道所有CheckBoxTreeItem<?> parent = (CheckBoxTreeItem<?>) item.getParent(); System.out.println(parent.isSelected()); 都是TreeItem的实例。


由于您还希望将CheckBoxTreeItem作为Label的一部分,因此可以简单地将TreeCell的图形设置为带有所需文本的CheckBoxTreeItem但是请注意,这会将Label置于实际复选框的 right 处。如代码中所示,如果您希望LabelLabel

在内部,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);
        }
    }

}

一些注意事项:

  1. 这取决于内部如何实现tree.setCellFactory(CustomCheckBoxTreeItem.forTreeView()); (特别是Java 10,但也可能与Java 8一起使用)。如果他们将来更改实施方式,则可能会失败

  2. 这要求您将CheckBoxTreeCell设置为Label的图形,而不是仅在TreeItem中使用它。如果您不想执行此操作,请删除使用TreeCell图形的行为,并将其替换为内部TreeItem。这将允许您删除对LabelgraphicListener的使用。如果您还想显示weakGraphicListener的图形,则还可以删除TreeItem

  3. 我不尝试缓存cBox.setGraphic(null)(或者如果您基于注释#2进行更改,则可能尝试HBox)。相反,我每次都只创建一个新的Label。更改代码应该很简单,因此它可以缓存HBox,但是,如果您选择


如果您不想使用HBox,而是想外部化是否选择了某个项目,则应查看以下方法:

如果您仍然希望将图形放在CheckBoxTreeItem上,则也必须在此处扩展CheckBox。上面的CheckBoxTreeCell的实现方式的唯一真正区别是,您必须提供一种将将以上两种方法使用的CustomCheckBoxTreeCell设置为Callback的方法。您可以通过公开采用CustomCheckBoxTreeCell的构造函数或通过在单元工厂中设置selectedStateCallback属性来实现此目的。