即使设置为不可折叠,TitledPane 的箭头也保持可见

问题描述 投票:0回答:2

我正在尝试创建一个 TitledPane,一旦打开并且其中的 TextField 没有有效输入,就无法折叠。

此行为已经在我的代码中起作用,但是一旦我将 collapsibleProperty 设置为 false,箭头按钮就会消失。

我宁愿让它保留并以禁用状态显示。我知道这可以通过伪类状态来实现,但是 TitledPaneSkin 类的 update() 函数中的以下代码阻止我使用这种方法,因为箭头甚至不会添加到 gui 中:

private void update() {
    getChildren().clear();
    final TitledPane titledPane = getSkinnable();

    if (titledPane.isCollapsible()) {
        getChildren().add(arrowRegion);
    }

...

我尝试编写一个扩展 TitledPaneSkin 的自定义 Skin 类,但定义 arrowRegion 的 TitleRegion 及其更新函数无法访问。

禁用 TitledPane 本身对我来说也不起作用,因为我需要其中的 TextField 可以访问/可编辑。

我还尝试将 TitledPane 标题的 -fx-graphic 属性设置为自定义图像,但对我来说没有成功。

.titled-pane .title {
    -fx-graphic: url("arrow.png");
}
java javafx accordion skin
2个回答
3
投票

这有点破解,但您可以在标题窗格中查找并禁用/启用标题。这不会影响标题窗格内容的禁用状态。


import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        VBox content = new VBox();
        TitledPane titledPane = new TitledPane("Test", content);
        titledPane.setExpanded(true);

        content.setAlignment(Pos.CENTER);
        content.setPadding(new Insets(20));
        TextField textField = new TextField();
        content.getChildren().addAll(new Label("Enter at least three characters:"),textField);
        textField.textProperty().addListener((obs, oldText, newText) -> updateTitledPaneState(titledPane, newText.length() >= 3));

        Scene scene = new Scene(titledPane);
        scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
        stage.setScene(scene);
        stage.show();

        updateTitledPaneState(titledPane, false);
    }

    private void updateTitledPaneState(TitledPane titledPane, boolean shouldBeCollapsible) {
        Node title = titledPane.lookup(".title");
        title.setDisable(! shouldBeCollapsible);
    }

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

默认情况下,标题在禁用时不知道更改其外观,因此您可以使用外部样式表添加它:

样式.css:

.titled-pane > .title:disabled {
    -fx-opacity: 0.4;
}

0
投票

+1 表示@James_D 的回答。

为了不同的方法或替代解决方案,使用他的演示代码,我尝试了以下解决方案。你可以看看。

在这种方法中,我创建了一个自定义

TitledPane
,它具有
invalid
属性。如果
invalid
属性为 true 并为某些自定义样式设置伪状态,我会阻止鼠标事件不折叠,而不是禁用。

这可能会添加一些额外的代码,但您可以考虑它的可重用性和自定义样式。

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.stream.Stream;

public class TitledPaneValidationDemo extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        VBox content = new VBox(10);
        content.setAlignment(Pos.CENTER);
        content.setPadding(new Insets(20));

        CustomTitledPane titledPane = new CustomTitledPane("Test", content);
        titledPane.setExpanded(true);

        TextField textField1 = new TextField();
        TextField textField2 = new TextField();
        TextField textField3 = new TextField();
        content.getChildren().addAll(new Label("Enter at least three characters in each box:"), textField1, textField2, textField3);
        Stream.of(textField1, textField2, textField3).forEach(tf -> {
            tf.textProperty().addListener(p -> invalidate(titledPane, textField1, textField2, textField3));
        });

        Scene scene = new Scene(titledPane);
        scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
        stage.setScene(scene);
        stage.setTitle("Titled Pane Demo");
        stage.show();
        invalidate(titledPane, textField1, textField2, textField3);
    }

    private void invalidate(CustomTitledPane titledPane, TextField textField1, TextField textField2, TextField textField3) {
        titledPane.setInvalid(textField1.getText().length() < 3 || textField2.getText().length() < 3 || textField3.getText().length() < 3);
    }

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

    class CustomTitledPane extends TitledPane {
        private BooleanProperty invalid = new SimpleBooleanProperty(false);

        private final PseudoClass PSEUDO_CLASS_INVALID = PseudoClass.getPseudoClass("invalid");
        private Node title;

        public CustomTitledPane(String text, Node node) {
            super(text, node);
            skinProperty().addListener((obs, old, val) -> title = null);
            invalidProperty().addListener((obs, old, val) -> pseudoClassStateChanged(PSEUDO_CLASS_INVALID, val));
        }

        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            if (title == null) {
                title = lookup(".title");
                title.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
                    if (isInvalid()) {
                        setExpanded(true);
                        e.consume();
                    }
                });
            }
        }

        public boolean isInvalid() {
            return invalid.get();
        }

        public BooleanProperty invalidProperty() {
            return invalid;
        }

        public void setInvalid(final boolean invalid) {
            this.invalid.set(invalid);
        }
    }
}

CSS:

.titled-pane:invalid > .title {
    -fx-color: #FF000050;
} 
© www.soinside.com 2019 - 2024. All rights reserved.