Ответ 1
Я создаю класс BorderedTitledPane
, который помещает обозначенную рамку вокруг содержимого.
Если вам не нужно, чтобы заголовок, помещающий границу вокруг вещей, еще проще - просто установите параметры границы css в регионе (например, -fx-border-color: black;
).
Вот полный исполняемый образец.
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
/** Places content in a bordered pane with a title. */
public class BorderedTitledPane extends StackPane {
private StringProperty title = new SimpleStringProperty();
private ObjectProperty<Node> graphic = new SimpleObjectProperty<>();
private ObjectProperty<Node> content = new SimpleObjectProperty<>();
private ObjectProperty<Pos> titleAlignment = new SimpleObjectProperty<>();
// todo other than TOP_LEFT other alignments aren't really supported correctly, due to translation fudge for indentation of the title label in css => best to implement layoutChildren and handle layout there.
// todo work out how to make content the default node for fxml so you don't need to write a <content></content> tag.
public BorderedTitledPane() {
this("", null);
}
public BorderedTitledPane(String titleString, Node contentNode) {
final Label titleLabel = new Label();
titleLabel.textProperty().bind(Bindings.concat(title, " "));
titleLabel.getStyleClass().add("bordered-titled-title");
titleLabel.graphicProperty().bind(graphic);
titleAlignment.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
StackPane.setAlignment(titleLabel, titleAlignment.get());
}
});
final StackPane contentPane = new StackPane();
getStyleClass().add("bordered-titled-border");
getChildren().addAll(titleLabel, contentPane);
content.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (content.get() == null) {
contentPane.getChildren().clear();
} else {
if (!content.get().getStyleClass().contains("bordered-titled-content")) {
content.get().getStyleClass().add("bordered-titled-content"); // todo would be nice to remove this style class when it is no longer required.
}
contentPane.getChildren().setAll(content.get());
}
}
});
titleAlignment.set(Pos.TOP_LEFT);
this.title.set(titleString);
this.content.set(contentNode);
}
public String getTitle() {
return title.get();
}
public StringProperty getTitleStringProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public Pos getTitleAlignment() {
return titleAlignment.get();
}
public ObjectProperty<Pos> titleAlignmentProperty() {
return titleAlignment;
}
public void setTitleAlignment(Pos titleAlignment) {
this.titleAlignment.set(titleAlignment);
}
public Node getContent() {
return content.get();
}
public ObjectProperty<Node> contentProperty() {
return content;
}
public void setContent(Node content) {
this.content.set(content);
}
public Node getGraphic() {
return graphic.get();
}
public ObjectProperty<Node> graphicProperty() {
return graphic;
}
public void setGraphic(Node graphic) {
this.graphic.set(graphic);
}
}
Связанный CSS.
.bordered-titled-title {
-fx-translate-x: 8;
-fx-translate-y: -10;
-fx-padding: 0 0 0 4;
-fx-background-color: -fx-background;
}
.bordered-titled-border {
-fx-content-display: top;
-fx-border-insets: 2 0 0 0;
-fx-border-color: -fx-text-box-border;
-fx-border-width: 2;
}
.bordered-titled-content {
-fx-padding: 18 5 5 5;
}