JDK10下JavaFX复选框选中样式滚动后失效问题求助
Ah, this is a classic gotcha with JavaFX's virtualized controls like ListView. The problem occurs because JavaFX only creates enough cells to fill the visible viewport; when you scroll, it reuses existing cells for new items instead of creating fresh ones. If you set the background style only once (like on initial creation), those reused cells won't update their style to match the new item's selected state.
Here's how to fix this properly:
1. Use a Data Model with Observable State
First, create a simple model class that exposes an observable selected property. This lets your cells react when the selection state changes (including when you trigger "select all"):
import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; public class SelectableItem { private final String label; private final BooleanProperty selected = new SimpleBooleanProperty(false); public SelectableItem(String label) { this.label = label; } // Getters and property accessors public String getLabel() { return label; } public boolean isSelected() { return selected.get(); } public void setSelected(boolean selected) { this.selected.set(selected); } public BooleanProperty selectedProperty() { return selected; } }
2. Customize the ListCell to Dynamically Update Styles
Instead of relying on static styling, create a custom ListCell that updates its style every time it's assigned a new item (or the item's selection state changes). This ensures reused cells get the correct style:
import javafx.scene.control.CheckBox; import javafx.scene.control.ListCell; public class CheckBoxListCell extends ListCell<SelectableItem> { private final CheckBox checkBox = new CheckBox(); private static final String SELECTED_STYLE_CLASS = "selected-checkbox-cell"; public CheckBoxListCell() { super(); // Bind checkbox state to the item's selected property checkBox.selectedProperty().addListener((obs, oldVal, newVal) -> { if (getItem() != null) { getItem().setSelected(newVal); } }); setGraphic(checkBox); setContentDisplay(ContentDisplay.RIGHT); } @Override protected void updateItem(SelectableItem item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { // Reset cell for empty state setText(null); checkBox.setSelected(false); getStyleClass().remove(SELECTED_STYLE_CLASS); } else { setText(item.getLabel()); // Sync checkbox state with the item (unbind first to avoid issues) checkBox.selectedProperty().unbind(); checkBox.setSelected(item.isSelected()); checkBox.selectedProperty().bind(item.selectedProperty()); // Update style based on current selection state updateCellStyle(item.isSelected()); // Listen for future selection changes to update style item.selectedProperty().addListener((obs, oldVal, newVal) -> { updateCellStyle(newVal); }); } } private void updateCellStyle(boolean isSelected) { if (isSelected) { getStyleClass().add(SELECTED_STYLE_CLASS); } else { getStyleClass().remove(SELECTED_STYLE_CLASS); } } }
3. Define Your Style in CSS
Use a CSS class instead of inline styles for cleaner, more maintainable code. Create a styles.css file in your resources folder:
.selected-checkbox-cell { -fx-background-color: #cceeff; /* Replace with your desired background color */ }
4. Wire It All Together in Your Dialog
Set up the ListView, "Select All" button, and dialog, making sure to apply the custom cell factory and load the CSS:
import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.ListView; import javafx.scene.layout.VBox; public class CheckBoxDialogExample { public static void showDialog() { // Create sample items ObservableList<SelectableItem> items = FXCollections.observableArrayList( new SelectableItem("Item 1"), new SelectableItem("Item 2"), new SelectableItem("Item 3"), // Add as many items as you need to test scrolling new SelectableItem("Item 4"), new SelectableItem("Item 5"), new SelectableItem("Item 6"), new SelectableItem("Item 7"), new SelectableItem("Item 8"), new SelectableItem("Item 9"), new SelectableItem("Item 10") ); // Set up ListView with custom cell factory ListView<SelectableItem> listView = new ListView<>(items); listView.setCellFactory(param -> new CheckBoxListCell()); // Select All button logic Button selectAllBtn = new Button("Select All"); selectAllBtn.setOnAction(e -> { items.forEach(item -> item.setSelected(true)); }); // Build dialog Dialog<Void> dialog = new Dialog<>(); dialog.setTitle("Checkbox List Dialog"); dialog.getDialogPane().setContent(new VBox(10, selectAllBtn, listView)); dialog.getDialogPane().getButtonTypes().add(ButtonType.OK); // Load CSS styles dialog.getDialogPane().getStylesheets().add( CheckBoxDialogExample.class.getResource("/styles.css").toExternalForm() ); dialog.showAndWait(); } }
Why This Works
- Virtualization Awareness: The custom
updateItemmethod runs every time a cell is reused, ensuring it always reflects the current item's state. - Observable State: By binding the checkbox to the item's
selectedProperty, any change (including from the "Select All" button) triggers an automatic update to the cell's style. - CSS Class Management: Adding/removing the style class instead of setting inline styles ensures clean state reset when cells are reused.
This solution is fully compatible with JDK 10, as all JavaFX APIs used here are available in that version.
内容的提问来源于stack exchange,提问作者TrackerSB

