JavaFX 8,带有复选框的 ListView

Posted

技术标签:

【中文标题】JavaFX 8,带有复选框的 ListView【英文标题】:JavaFX 8, ListView with Checkboxes 【发布时间】:2015-05-04 19:34:44 【问题描述】:

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

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

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

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

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

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

【问题讨论】:

【参考方案1】:

如果您有一个ListView&lt;String&gt;,那么ListView 中的每个项目都是一个String,而CheckBoxListCell.forListView(...) 方法需要一个Callback&lt;String, ObservableValue&lt;Boolean&gt;&gt;

在 Java 8 之前的想法中,Callback&lt;String, ObservableValue&lt;Boolean&gt;&gt; 是定义单个方法的接口,

public ObservableValue<Boolean> call(String s) ;

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

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

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

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

这里的典型用例是ListView 中的项目类型将BooleanProperty 作为其状态的一部分。因此,您通常会将其与某种表示您的数据的自定义类一起使用,如下所示与内部 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);
    

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

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

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

在 Java 8 中,您可以简化代码。因为Callback 接口只有一个抽象方法(使其成为函数接口),您可以将Callback&lt;Item, ObservableValue&lt;Boolean&gt;&gt; 视为一个函数,它接受Item 并生成ObservableValue&lt;Boolean&gt;。所以第一个例子中的单元工厂可以写成lambda expression:

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

或者,更简洁地使用method references:

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

【讨论】:

哇,我真的不得不说这种帮助不仅很好,而且很棒!我理解你写的一切,它实际上以任何方式解决了我的问题。非常感谢詹姆斯!我会尽快投票(因为我是新手,我还缺少 15 个声望)【参考方案2】:
    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 ;
        
    ));

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

【讨论】:

如果我限制列表高度,这有一个缺陷。我有数百个列表项。我使用检查从顶部选择一些项目,当我向下滚动到底部然后向上滚动时,顶部选定的项目被刷新并取消选中。【参考方案3】:

感谢您之前的回答。 我错过了不需要 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 ;
        
    ));

【讨论】:

以上是关于JavaFX 8,带有复选框的 ListView的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式选择时,CheckedTextView 未绘制复选标记

带有复选框的 JavaFX 组合框

带有复选标记和详细信息披露的表格视图

在 CheckListView JavaFX 上实现全选

如何编码以选择带有复选标记的多行?

带有复选标记的 UITableViewCell 不可见