javafx css仅适用于当前显示的元素

时间:2018-04-09 06:35:39

标签: css javafx

我正在尝试实现一个非常基本,简单和安静的典型对话框,其中显示了一个复选框列表和一个用于选择列表中所有这些复选框的按钮。

实际选择按预期工作。我还更改了显示复选符号的实际框的背景颜色。但是这种颜色仅适用于可见的复选框而不滚动列表。向下滚动列表时,您可以看到所有复选框都已选中,但未设置颜色。

我正在使用JDK10。

如何强制应用颜色?

编辑: 首次向下滚动然后单击“全选”时,前一个复选框的所有颜色也会正确设置。但仍然不是以下几个。

编辑: 使用instanceof过滤框并使用Region#setBackground(...)更改颜色时会出现相同的效果。

package javafxcssbroken;

import java.util.StringJoiner;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class JavaFXCssBroken extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<CheckBox> listView = new ListView();
        for (int i = 0; i < 100; i++) {
            listView.getItems().add(new CheckBox("Element " + i));
        }

        Button markAll = new Button("Select all");
        markAll.setOnAction(aevt -> {
            Color newColor = Color.BLUE;
            listView.getItems().stream()
                    .forEach(checkbox -> {
                        checkbox.setSelected(true);
                        checkbox.getChildrenUnmodifiable().stream()
                                .forEach(child -> child.setStyle(new StringJoiner(", ", "-fx-background-color: rgba(", ")")
                                .add(Double.toString(255 * newColor.getRed()))
                                .add(Double.toString(255 * newColor.getGreen()))
                                .add(Double.toString(255 * newColor.getBlue()))
                                .add(Double.toString(newColor.getOpacity()))
                                .toString()));
                    });
        });

        VBox vbox = new VBox(listView, markAll);

        StackPane root = new StackPane();
        root.getChildren().add(vbox);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("JavaFXCssBroken");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

编辑: 因此,解决方案应该是引入一个表示数据本身的类以及在评论中讨论的关联。

JavaFXCssBroken.java

package javafxcssbroken;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javax.swing.text.StyleConstants;

/**
 * @param <I> The type of the items to display.
 * @param <G> The type of the groups to associate items with.
 */
public class JavaFXCssBroken<I, G> extends Application {

    private ListView<AssociatedItem<I, G>> listView;
    private Set<I> items;
    private Map<G, Color> groups;
    private Optional<G> currentGroup;

    public JavaFXCssBroken(Set<I> items, Map<G, Color> groups, Optional<G> initialGroup){
        this.items = items;
        this.groups = groups;
        this.currentGroup = initialGroup;
    }

    @Override
    public void start(Stage primaryStage) {
        listView = new ListView();
        listView.setCellFactory(lv -> {
            ListCell<AssociatedItem<I, G>> cell = new ListCell<>();
            cell.itemProperty().addListener((obs, oldVal, newVal) -> {
                if(!cell.isEmpty() && newVal != null && newVal.getGroup().isPresent()){
                    cell.setBackground(new Background(
                            new BackgroundFill(groups.get(newVal.getGroup().get()), CornerRadii.EMPTY, Insets.EMPTY)));
                }
            });
            cell.emptyProperty().addListener((obs, oldVal, newVal) -> {
                if(newVal){
                    cell.setBackground(Background.EMPTY);
                }
            });
            return cell;
        });

        items.stream().forEach(item -> listView.getItems().add(new AssociatedItem(item)));

        Button markAll = new Button("Select all");
        markAll.setOnAction(aevt -> {
            listView.getItems().stream()
                    .forEach(item -> item.setGroup(currentGroup));
        });

        VBox vbox = new VBox(listView, markAll);

        StackPane root = new StackPane();
        root.getChildren().add(vbox);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("JavaFXCssBroken");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

AssociatedItem.java

package javafxcssbroken;

import java.util.Optional;

public class AssociatedItem<I, G> {

    private I item;
    private Optional<G> group;

    public AssociatedItem(I item) {
        this.item = item;
        group = Optional.empty();
    }

    public I getItem() {
        return item;
    }

    public void setItem(I item) {
        this.item = item;
    }

    public Optional<G> getGroup() {
        return group;
    }

    public void setGroup(Optional<G> group) {
        this.group = group;
    }

    @Override
    public String toString() {
        return item.toString();
    }
}

Main.java

package javafxcssbroken;

import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javafx.application.Application;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application{

    @Override
    public void start(Stage primaryStage) {
        Map<Integer, Color> groups = Map.of(0, Color.RED, 1, Color.BLUE);
        Set<String> items = new HashSet<>();
        for (int i = 0; i < 100; i++) {
            items.add("Elements " + i);
        }
        new JavaFXCssBroken(items, groups, Optional.of(0)).start(primaryStage);
    }

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

此时颜色设置为所有元素,但仅在开始滚动时应用颜色更新。是否足以将AssociatedItems引入Observable?

2 个答案:

答案 0 :(得分:1)

Kleopatra是对的。但是假设它是一个简单的例子或训练(即使你应该尽快采取良好的做法,即使在训练期间),只需使用css strength,创建一个包含这个内容的样式表:

StyleCheckbox.css

dict

通过评论无用的机制,并添加刚刚创建的样式表来修改现有代码。 :

.check-box:selected >  .box {
   -fx-background-color:blue;
}

答案 1 :(得分:0)

正如kleopatra建议覆盖updateItem(...)并使用提取器一样。

Main.java与问题的上一次编辑保持一致。

AssociatedItem.java

package javafxcssbroken;

import java.util.Optional;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

public class AssociatedItem<I, G> {

    private final ObjectProperty<I> item = new SimpleObjectProperty<>();
    private final ObjectProperty<Optional<G>> group = new SimpleObjectProperty<>();

    public AssociatedItem(I item) {
        this.item.set(item);
        group.set(Optional.empty());
    }

    public ObjectProperty<I> itemProperty() {
        return item;
    }

    public I getItem() {
        return itemProperty().get();
    }

    public void setItem(I item) {
        this.item.set(item);
    }

    public ObjectProperty<Optional<G>> groupProperty() {
        return group;
    }

    public Optional<G> getGroup() {
        return groupProperty().get();
    }

    public void setGroup(Optional<G> group) {
        this.group.set(group);
    }
}

JavaFXCssBroken.java

package javafxcssbroken;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * @param <I> The type of the items to display.
 * @param <G> The type of the groups to associate items with.
 */
public class JavaFXCssBroken<I, G> extends Application {

    private ListView<AssociatedItem<I, G>> listView;
    private Set<I> items;
    private ObservableValue<ObservableList<AssociatedItem<I, G>>> associatedItems
            = new SimpleObjectProperty<>(
                    FXCollections.observableArrayList(i -> new Observable[]{i.itemProperty(), i.groupProperty()}));
    private Map<G, Color> groups;
    private Optional<G> currentGroup;

    public JavaFXCssBroken(Set<I> items, Map<G, Color> groups, Optional<G> initialGroup) {
        this.items = items;
        this.groups = groups;
        this.currentGroup = initialGroup;
    }

    @Override
    public void start(Stage primaryStage) {
        listView = new ListView();
        listView.setCellFactory(lv -> new ListCell<AssociatedItem<I, G>>() {
            @Override
            protected void updateItem(AssociatedItem<I, G> item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setBackground(Background.EMPTY);
                } else {
                    item.getGroup().ifPresent(group -> {
                        setBackground(new Background(
                                new BackgroundFill(groups.get(group), CornerRadii.EMPTY, Insets.EMPTY)));
                    });
                    setText(item.getItem().toString());
                }
            }
        });

        listView.itemsProperty().bind(associatedItems);

        items.stream().forEach(item -> {
            associatedItems.getValue().add(new AssociatedItem<>(item));
        });

        Button markAll = new Button("Select all");
        markAll.setOnAction(aevt -> {
            listView.getItems().stream()
                    .forEach(item -> item.setGroup(currentGroup));
        });

        VBox vbox = new VBox(listView, markAll);

        StackPane root = new StackPane();
        root.getChildren().add(vbox);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("JavaFXCssBroken");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}