JavaFX 8,带复选框的 ListView

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28843858/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:55:47  来源:igfitidea点击:

JavaFX 8, ListView with Checkboxes

javalistviewcheckboxjavafx

提问by jan

I want to create a simple ListView. I have figured out I can use the method setCellFactory() but I don't understand how to use them correctly. So far I have:

我想创建一个简单的 ListView。我发现我可以使用 setCellFactory() 方法,但我不明白如何正确使用它们。到目前为止,我有:

myListView.setCellFactory(CheckBoxListCell.forListView(property));

With "property" being something called a Callback--I think Callback has something to do with bidirectional bounding. So I created a

“属性”被称为回调——我认为回调与双向边界有关。所以我创建了一个

property = new CallBack<String, ObservableValue<Boolean>>();

My compiler is telling me if I create a new Callback, I need to overwrite the method call.

我的编译器告诉我,如果我创建了一个新的回调,我需要覆盖方法调用。

And here I am stuck. What do I do with that method call? I can implement it, but what should I return, or use it for? I want to click my checkbox on any listItem and have it display "hi" in console.

我被困在这里。我该怎么处理那个方法调用?我可以实现它,但我应该返回什么,或者用它做什么?我想在任何 listItem 上单击我的复选框,并让它在控制台中显示“hi”。

回答by James_D

If you have a ListView<String>, then each item in the ListViewis a String, and the CheckBoxListCell.forListView(...)method expects a Callback<String, ObservableValue<Boolean>>.

如果您有 a ListView<String>,则 中的每一项ListView都是 a String,并且该CheckBoxListCell.forListView(...)方法需要 a Callback<String, ObservableValue<Boolean>>

In the pre-Java 8 way of thinking of things, a Callback<String, ObservableValue<Boolean>>is an interface that defines a single method,

在 Java 8 之前的思维方式中,aCallback<String, ObservableValue<Boolean>>是定义单个方法的接口,

public ObservableValue<Boolean> call(String s) ;

So you need something that implements that interface, and you pass in an object of that type.

因此,您需要实现该接口的东西,并传入该类型的对象。

The documentationalso tells you how that callback is used:

文档还告诉您如何使用该回调:

A Callback that, given an object of type T (which is a value taken out of the ListView.items list), will return an ObservableValue that represents whether the given item is selected or not. This ObservableValue will be bound bidirectionally (meaning that the CheckBox in the cell will set/unset this property based on user interactions, and the CheckBox will reflect the state of the ObservableValue, if it changes externally).

一个回调函数,给定一个类型为 T 的对象(它是从 ListView.items 列表中取出的一个值),将返回一个 ObservableValue 表示是否选择了给定的项目。这个 ObservableValue 将被双向绑定(意味着单元格中的 CheckBox 将根据用户交互设置/取消设置此属性,如果它在外部发生变化,CheckBox 将反映 ObservableValue 的状态)。

(Since you have a ListView<String>, here Tis String.) So, for each element in the list view (each element is a String), the callback is used to determine an ObservableValue<Boolean>which is bidirectionally bound to the state of the checkbox. I.e. if the checkbox is checked, that property is set to true, and if unchecked it is set to false. Conversely, if the property is set to true(or false) programmatically, the checkbox is checked (or unchecked).

(因为你有一个ListView<String>,这里TString。)所以,对于列表视图中的每个元素(每个元素都是一个String),回调用于确定一个ObservableValue<Boolean>双向绑定到复选框的状态。即,如果复选框被选中,则该属性设置为true,如果未选中则设置为false。相反,如果以编程方式将属性设置为true(或false),则选中(或取消选中)复选框。

The typical use case here is that the type of item in the ListViewwould have a BooleanPropertyas part of its state. So you would typically use this with some kind of custom class representing your data, as follows with the inner Itemclass:

这里的典型用例是 中的项目类型ListViewBooleanProperty作为其状态的一部分。因此,您通常会将它与某种表示您的数据的自定义类一起使用,如下所示与内部Item类:

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.CheckBoxListCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ListViewWithCheckBox extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<Item> listView = new ListView<>();
        for (int i=1; i<=20; i++) {
            Item item = new Item("Item "+i, false);

            // observe item's on property and display message if it changes:
            item.onProperty().addListener((obs, wasOn, isNowOn) -> {
                System.out.println(item.getName() + " changed on state from "+wasOn+" to "+isNowOn);
            });

            listView.getItems().add(item);
        }

        listView.setCellFactory(CheckBoxListCell.forListView(new Callback<Item, ObservableValue<Boolean>>() {
            @Override
            public ObservableValue<Boolean> call(Item item) {
                return item.onProperty();
            }
        }));

        BorderPane root = new BorderPane(listView);
        Scene scene = new Scene(root, 250, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final BooleanProperty on = new SimpleBooleanProperty();

        public Item(String name, boolean on) {
            setName(name);
            setOn(on);
        }

        public final StringProperty nameProperty() {
            return this.name;
        }

        public final String getName() {
            return this.nameProperty().get();
        }

        public final void setName(final String name) {
            this.nameProperty().set(name);
        }

        public final BooleanProperty onProperty() {
            return this.on;
        }

        public final boolean isOn() {
            return this.onProperty().get();
        }

        public final void setOn(final boolean on) {
            this.onProperty().set(on);
        }

        @Override
        public String toString() {
            return getName();
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}

If you genuinely have a ListView<String>, it's not really clear what the property you are setting by clicking on the check box would be. But there's nothing to stop you creating one in the callback just for the purpose of binding to the check box's selected state:

如果您真的有ListView<String>,则不太清楚您通过单击复选框设置的属性是什么。但是没有什么可以阻止您在回调中创建一个只是为了绑定到复选框的选中状态:

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.CheckBoxListCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ListViewWithStringAndCheckBox extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<String> listView = new ListView<>();
        for (int i = 1; i <= 20 ; i++) {
            String item = "Item "+i ;
            listView.getItems().add(item);
        }

        listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
            @Override
            public ObservableValue<Boolean> call(String item) {
                BooleanProperty observable = new SimpleBooleanProperty();
                observable.addListener((obs, wasSelected, isNowSelected) -> 
                    System.out.println("Check box for "+item+" changed from "+wasSelected+" to "+isNowSelected)
                );
                return observable ;
            }
        }));

        BorderPane root = new BorderPane(listView);
        Scene scene = new Scene(root, 250, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Notice that in this case, the BooleanPropertys are potentially being created and discarded frequently. This probably isn't a problem in practice, but it does mean the first version, with the dedicated model class, may perform better.

请注意,在这种情况下,BooleanProperty可能会频繁地创建和丢弃 s。这在实践中可能不是问题,但这确实意味着具有专用模型类的第一个版本可能会表现得更好。

In Java 8, you can simplify the code. Because the Callbackinterface has only one abstract method (making it a Functional Interface), you can think of a Callback<Item, ObservableValue<Boolean>>as a function which takes a Itemand generates an ObservableValue<Boolean>. So the cell factory in the first example could be written with a lambda expression:

在 Java 8 中,您可以简化代码。因为Callback接口只有一个抽象方法(使其成为Functional Interface),您可以将 aCallback<Item, ObservableValue<Boolean>>视为一个函数,它接受 aItem并生成一个ObservableValue<Boolean>。因此,第一个示例中的细胞工厂可以用lambda 表达式编写:

    listView.setCellFactory(CheckBoxListCell.forListView(item -> item.onProperty()));

or, even more succinctly using method references:

或者,更简洁地使用方法引用

    listView.setCellFactory(CheckBoxListCell.forListView(Item::onProperty));

回答by James_D

    listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
        @Override
        public ObservableValue<Boolean> call(String item) {
            BooleanProperty observable = new SimpleBooleanProperty();
            observable.addListener((obs, wasSelected, isNowSelected) -> 
                System.out.println("Check box for "+item+" changed from "+wasSelected+" to "+isNowSelected)
            );
            return observable ;
        }
    }));

Thank you! This helps me to solve my problem.

谢谢!这有助于我解决我的问题。

回答by karnbo

Thanks for previous answers. I miss the information that setCellValueFactory is not needed, but value assigned should also be done in setCellFactory. Here is my approach (much copied from previous solution).

感谢之前的回答。我想念不需要 setCellValueFactory 的信息,但分配的值也应该在 setCellFactory 中完成。这是我的方法(从以前的解决方案中大量复制)。

public TreeTableColumn<RowContainer, Boolean> treetblcolHide;
...
    treetblcolHide.setCellFactory(CheckBoxTreeTableCell.<RowContainer, Boolean>forTreeTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
        @Override
        public ObservableValue<Boolean> call(final Integer param) {
            final RowContainer rowitem = treetblcolHide.getTreeTableView().getTreeItem(param).getValue();
            BooleanProperty observable = new SimpleBooleanProperty();
            observable.addListener(new ChangeListener<Boolean>() {
                                       @Override
                                       public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                                           rowitem.setHideMenuItem(newValue.toString());
                                       }
                                   }
            );
            observable.setValue(Boolean.parseBoolean(rowitem.getHideMenuItem()));
            return observable ;
        }
    }));