我正在尝试创建一个 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");
}
这有点破解,但您可以在标题窗格中查找并禁用/启用标题。这不会影响标题窗格内容的禁用状态。
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;
}
+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;
}