如何为已修改值的CheckBoxTreeItem及其子项设置文本样式
解决方案
要实现仅为修改后的CheckBoxTreeItem设置文本颜色,核心是给目标项添加状态标记,再通过自定义TreeCell绑定CSS伪类来精准控制样式,具体步骤如下:
1. 给TreeItem添加状态标记
为每个CheckBoxTreeItem添加可观察的BooleanProperty,标记该项是否已被修改/移动:
private void addModifiedProperty(CheckBoxTreeItem<String> item) { BooleanProperty modified = new SimpleBooleanProperty(false); item.getProperties().put("modified", modified); }
初始化所有TreeItem时调用此方法,给它们设置初始为false的状态标记。
2. 自定义TreeCell绑定CSS伪类
使用CSS伪类能更灵活管理样式,避免硬编码颜色。先定义伪类,再在TreeCell中根据标记属性切换伪类状态:
private static final PseudoClass MODIFIED_PSEUDO_CLASS = PseudoClass.getPseudoClass("modified"); treeView.setCellFactory(param -> { CheckBoxTreeCell<String> cell = new CheckBoxTreeCell<>(); cell.itemProperty().addListener((obs, oldItem, newItem) -> { if (newItem != null) { BooleanProperty modified = (BooleanProperty) newItem.getProperties().get("modified"); if (modified != null) { // 初始化时设置伪类状态 cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, modified.get()); // 监听标记属性变化,动态切换伪类 modified.addListener((obs2, oldVal, newVal) -> { cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, newVal); }); } else { cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, false); } } }); return cell; });
3. 添加CSS样式
通过CSS给带有伪类的单元格设置绿色文本:
.tree-cell:modified { -fx-text-fill: green; }
可以将CSS写入单独的.css文件,或像示例中直接嵌入代码。
4. 移动项时标记状态
在移动按钮的点击事件中,将目标项的modified属性设为true,触发样式更新:
moveBtn.setOnAction(e -> { // 假设从leftTreeView移动选中项到rightTreeView TreeView<String> leftTree = ...; TreeView<String> rightTree = ...; CheckBoxTreeItem<String> selectedNode = (CheckBoxTreeItem<String>) leftTree.getSelectionModel().getSelectedItem(); // 移除原TreeView的项 ((CheckBoxTreeItem<String>)leftTree.getRoot()).getChildren().remove(selectedNode); // 添加到目标TreeView ((CheckBoxTreeItem<String>)rightTree.getRoot()).getChildren().add(selectedNode); // 标记为已修改 BooleanProperty modified = (BooleanProperty) selectedNode.getProperties().get("modified"); if (modified != null) { modified.set(true); // 触发值变化事件确保视图更新 Event.fireEvent(selectedNode, new TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedNode, selectedNode.getValue())); } });
完整可运行示例
import javafx.application.Application; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.css.PseudoClass; import javafx.event.Event; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.stage.Stage; public class DualTreeViewDemo extends Application { private static final PseudoClass MODIFIED_PSEUDO_CLASS = PseudoClass.getPseudoClass("modified"); @Override public void start(Stage primaryStage) { try { // 左侧TreeView初始化 CheckBoxTreeItem<String> leftRoot = new CheckBoxTreeItem<>("源列表"); leftRoot.setExpanded(true); CheckBoxTreeItem<String> item1 = new CheckBoxTreeItem<>("English"); CheckBoxTreeItem<String> item2 = new CheckBoxTreeItem<>("Spanish"); CheckBoxTreeItem<String> item3 = new CheckBoxTreeItem<>("German"); CheckBoxTreeItem<String> item4 = new CheckBoxTreeItem<>("French"); addModifiedProperty(item1); addModifiedProperty(item2); addModifiedProperty(item3); addModifiedProperty(item4); leftRoot.getChildren().addAll(item1, item2, item3, item4); TreeView<String> leftTree = new TreeView<>(leftRoot); leftTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); leftTree.setCellFactory(getCheckBoxTreeCellFactory()); leftTree.setMaxHeight(140); leftTree.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); // 右侧TreeView初始化 CheckBoxTreeItem<String> rightRoot = new CheckBoxTreeItem<>("目标列表"); rightRoot.setExpanded(true); TreeView<String> rightTree = new TreeView<>(rightRoot); rightTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); rightTree.setCellFactory(getCheckBoxTreeCellFactory()); rightTree.setMaxHeight(140); rightTree.setBorder(new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); // 移动按钮 Button moveBtn = new Button("→ 移动选中项 →"); moveBtn.setOnAction(e -> { CheckBoxTreeItem<String> selectedNode = (CheckBoxTreeItem<String>) leftTree.getSelectionModel().getSelectedItem(); if (selectedNode != null && !selectedNode.equals(leftRoot)) { leftRoot.getChildren().remove(selectedNode); rightRoot.getChildren().add(selectedNode); // 标记为已修改 BooleanProperty modified = (BooleanProperty) selectedNode.getProperties().get("modified"); if (modified != null) { modified.set(true); Event.fireEvent(selectedNode, new TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedNode, selectedNode.getValue())); } } }); // 布局 HBox treeBox = new HBox(20, leftTree, moveBtn, rightTree); VBox rootBox = new VBox(10, new Label("双TreeView移动示例"), treeBox); rootBox.setPadding(new Insets(25)); Scene scene = new Scene(new Group(rootBox), 650, 250, Color.BEIGE); // 添加CSS样式 scene.getStylesheets().add("data:text/css," + ".tree-cell:modified {-fx-text-fill: green;}"); primaryStage.setTitle("TreeView移动项样式示例"); primaryStage.setScene(scene); primaryStage.show(); } catch(Exception e) { e.printStackTrace(); } } private TreeCellFactory<TreeView<String>, String> getCheckBoxTreeCellFactory() { return param -> { CheckBoxTreeCell<String> cell = new CheckBoxTreeCell<>(); cell.itemProperty().addListener((obs, oldItem, newItem) -> { if (newItem != null) { BooleanProperty modified = (BooleanProperty) newItem.getProperties().get("modified"); if (modified != null) { cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, modified.get()); modified.addListener((obs2, oldVal, newVal) -> { cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, newVal); }); } else { cell.pseudoClassStateChanged(MODIFIED_PSEUDO_CLASS, false); } } }); return cell; }; } private void addModifiedProperty(CheckBoxTreeItem<String> item) { BooleanProperty modified = new SimpleBooleanProperty(false); item.getProperties().put("modified", modified); } public static void main(String[] args) { launch(args); } }
内容的提问来源于stack exchange,提问作者Srikant
相关产品推荐
相关产品推荐

