JavaFx:ListView CheckBoxListCell 选择
JavaFx: ListView CheckBoxListCell selection
我在 ListView
使用 CheckBoxListCell
时遇到问题。当我 check/uncheck 一个项目时,该项目不是预期的 selected/focused,因为 CheckBox 也是项目的一部分,而不仅仅是文本部分。
这是一个简单的代码,您可以验证它。
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.CheckBoxListCell;
import lombok.Getter;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML private ListView<Model> listView;
@Override
public void initialize(URL location, ResourceBundle resources) {
// without selection
// listView.setCellFactory(CheckBoxListCell.forListView(Model::getSelected));
// actual "bad" solution
listView.setCellFactory(factory -> {
CheckBoxListCell<Model> cell = new CheckBoxListCell<Model>() {
@Override
public void updateItem(Model item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
return;
}
((CheckBox) getGraphic()).selectedProperty().addListener(
(observable, oldValue, newValue) -> listView.getSelectionModel().select(getItem()));
}
};
cell.setSelectedStateCallback(Model::getSelected);
return cell;
});
ObservableList<Model> items = FXCollections.observableArrayList();
items.add(new Model("A", true));
items.add(new Model("B", true));
items.add(new Model("C", false));
listView.setItems(items);
}
@Getter
private class Model {
String text;
BooleanProperty selected;
private Model(String text, Boolean selected) {
this.text = text;
this.selected = new SimpleBooleanProperty(selected);
}
@Override public String toString() {
return text;
}
}
}
.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.ListView?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="list.Controller">
<ListView fx:id="listView"/>
</AnchorPane>
如您所见,我找到了一个解决方案,或者更确切地说是一个肮脏的解决方法,但我不太喜欢它,因为它在每个 updateItem 时都会被调用,并且它会添加监听器 n 次不是很好。
任何其他 ideas/solutions 当我 check/uncheck 整个项目的组合框是 selected/focused.
时,我如何实现这一点
这个答案只涵盖了问题的一部分:如何确保单元格图形 属性 上的侦听器仅注册一次(如果我们无法控制图形何时显示已设置)。
涉及的步骤:
- 定义一个安装 "real" 侦听器的 InvalidationListener(注意:必须是一个字段,以便稍后能够删除)
- 在实例化时
在单元格的图形 属性 上注册侦听器
- 实现"real"方法的注册,删除初始图形监听器,安装需要的东西
代码片段(注意:监听选中的 属性 不是一个好主意,因为它会在数据更改时触发,而不仅仅是在用户单击复选框时触发!):
listView.setCellFactory(factory -> {
CheckBoxListCell<Model> cell = new CheckBoxListCell<Model>() {
// a listener on the graphicProperty: it installs the "real" listener
InvalidationListener graphicListener = g -> {
// installs the "real" listener on the graphic control once it is available
registerUIListener();
};
{
// install the graphic listener at instantiation
graphicProperty().addListener(graphicListener);
}
/** method to install a listener on a property of the graphic control
* and unregisters the initially installed listener
*/
private void registerUIListener() {
if (!(getGraphic() instanceof CheckBox)) throw new IllegalStateException("checkBox expected");
graphicProperty().removeListener(graphicListener);
((CheckBox) getGraphic()).selectedProperty().addListener(
(observable, oldValue, newValue) -> listView.getSelectionModel().select(getItem()));
}
};
cell.setSelectedStateCallback(Model::selectedProperty);
return cell;
});
我在 ListView
使用 CheckBoxListCell
时遇到问题。当我 check/uncheck 一个项目时,该项目不是预期的 selected/focused,因为 CheckBox 也是项目的一部分,而不仅仅是文本部分。
这是一个简单的代码,您可以验证它。
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.CheckBoxListCell;
import lombok.Getter;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML private ListView<Model> listView;
@Override
public void initialize(URL location, ResourceBundle resources) {
// without selection
// listView.setCellFactory(CheckBoxListCell.forListView(Model::getSelected));
// actual "bad" solution
listView.setCellFactory(factory -> {
CheckBoxListCell<Model> cell = new CheckBoxListCell<Model>() {
@Override
public void updateItem(Model item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
return;
}
((CheckBox) getGraphic()).selectedProperty().addListener(
(observable, oldValue, newValue) -> listView.getSelectionModel().select(getItem()));
}
};
cell.setSelectedStateCallback(Model::getSelected);
return cell;
});
ObservableList<Model> items = FXCollections.observableArrayList();
items.add(new Model("A", true));
items.add(new Model("B", true));
items.add(new Model("C", false));
listView.setItems(items);
}
@Getter
private class Model {
String text;
BooleanProperty selected;
private Model(String text, Boolean selected) {
this.text = text;
this.selected = new SimpleBooleanProperty(selected);
}
@Override public String toString() {
return text;
}
}
}
.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.ListView?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="list.Controller">
<ListView fx:id="listView"/>
</AnchorPane>
如您所见,我找到了一个解决方案,或者更确切地说是一个肮脏的解决方法,但我不太喜欢它,因为它在每个 updateItem 时都会被调用,并且它会添加监听器 n 次不是很好。
任何其他 ideas/solutions 当我 check/uncheck 整个项目的组合框是 selected/focused.
时,我如何实现这一点这个答案只涵盖了问题的一部分:如何确保单元格图形 属性 上的侦听器仅注册一次(如果我们无法控制图形何时显示已设置)。
涉及的步骤:
- 定义一个安装 "real" 侦听器的 InvalidationListener(注意:必须是一个字段,以便稍后能够删除)
- 在实例化时 在单元格的图形 属性 上注册侦听器
- 实现"real"方法的注册,删除初始图形监听器,安装需要的东西
代码片段(注意:监听选中的 属性 不是一个好主意,因为它会在数据更改时触发,而不仅仅是在用户单击复选框时触发!):
listView.setCellFactory(factory -> {
CheckBoxListCell<Model> cell = new CheckBoxListCell<Model>() {
// a listener on the graphicProperty: it installs the "real" listener
InvalidationListener graphicListener = g -> {
// installs the "real" listener on the graphic control once it is available
registerUIListener();
};
{
// install the graphic listener at instantiation
graphicProperty().addListener(graphicListener);
}
/** method to install a listener on a property of the graphic control
* and unregisters the initially installed listener
*/
private void registerUIListener() {
if (!(getGraphic() instanceof CheckBox)) throw new IllegalStateException("checkBox expected");
graphicProperty().removeListener(graphicListener);
((CheckBox) getGraphic()).selectedProperty().addListener(
(observable, oldValue, newValue) -> listView.getSelectionModel().select(getItem()));
}
};
cell.setSelectedStateCallback(Model::selectedProperty);
return cell;
});