java - 在扩展皮肤中调整 TextArea 的大小
问题描述
在 JavaFX 中,我正在尝试对 TextArea 进行扩展,这样如果在 TextArea 的底部有一个额外指定的 String 参数,则该文本将显示为提示。
这很容易在单独的窗格上实现,当他向属性添加一个侦听器并在下面添加一个标签时。
我尝试以更常规的方式进行:我创建了扩展 TextArea 和 TextAreaSkin 的类,并将 StringProperty“提示”添加到 TextArea 类。在 TextAreaSkin 中为此属性添加了一个侦听器:
public TextValueSkin(final TextValue control) {
super(control);
this.textValue = control;
registerChangeListener(textValue.hintProperty(), e -> {
updateHint();
Platform.runLater(() -> {
control.requestLayout();
if (control.getParent() != null) {
control.getParent().requestLayout();
}
});
});
}
并覆盖方法 layoutChildren (double x, double y, double w, double h):
protected void layoutChildren(double x, double y, double w, double h) {
final double bottomHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.prefHeight(w));
double textHeight = h - snapSizeY(bottomHeight);
super.layoutChildren(x, y, w, textHeight);
if (lblHint != null) {
lblHint.resizeRelocate(x, textHeight, w, bottomHeight);
}
}
因此,我得到了以下类型的带有提示的 TextArea:[在此处输入图像描述]
也就是说,ScrollPane 和文本内容采用了新尺寸(减去 Label 高度),浅色边框(与 ContentView 相关)采用了新尺寸,但白色背景本身和浅色边框保持相同尺寸。
请帮我调整以降低此背景和边框的高度。
下面的完整演示。当 TextValue 聚焦时,我的问题是可见的(请参阅随附的屏幕截图)。提示位于明亮的边框内,并在可编辑文本的白色背景下,尽管它不以任何方式应用于它。
主类:
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception {
TextValue textValue = new TextValue();
textValue.setText("Text");
Scene scene = new Scene(textValue);
stage.setScene(scene);
stage.show();
Platform.runLater(() -> textValue.setHint("HINT"));
}
}
文本值类:
public class TextValue extends TextArea {
private StringProperty hint = new SimpleStringProperty(this, "hint");
public ReadOnlyStringProperty hintProperty() {
return hint;
}
public void setHint(String hint) {
this.hint.set(hint);
}
@Override
protected Skin<?> createDefaultSkin() {
return new TextValueSkin(this);
}
}
TextValueSkin.class:
public class TextValueSkin extends TextAreaSkin {
final private TextValue textValue;
private Label lblHint;
public TextValueSkin(TextValue control) {
super(control);
this.textValue = control;
registerChangeListener(textValue.hintProperty(), e -> {
updateHint();
Platform.runLater(() -> {
control.requestLayout();
if (control.getParent() != null) {
control.getParent().requestLayout();
}
});
});
}
private void updateHint() {
getChildren().remove(lblHint);
String hint = textValue.hintProperty().get();
if (hint != null && !hint.isEmpty()) {
lblHint = new Label(hint);
lblHint.getStyleClass().add("hint");
lblHint.setManaged(false);
getChildren().add(lblHint);
} else {
lblHint = null;
}
}
@Override
protected void layoutChildren(double x, double y, double w, double h) {
final double bottomHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.prefHeight(w));
double textHeight = h - snapSizeY(bottomHeight);
super.layoutChildren(x, y, w, textHeight);
if (lblHint != null) {
lblHint.resizeRelocate(x, textHeight, w, bottomHeight);
}
}
}
解决方案
这是从 TextArea 的默认样式扩展而来的行为。突出显示滚动条周围的边框是必需的。我重写了默认样式,以便突出显示的边框指的是 ScrollPane 而不是 TextArea。
CSS:
.vbox-demo {
-fx-padding: 1em 1em 1em 1em;
-fx-spacing: 1em;
}
.text-value {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-background-radius: 0;
-fx-cursor: default;
-fx-padding: 0;
}
.text-value:focused {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-background-radius: 0;
}
.text-value .scroll-pane:disabled {
-fx-background-color: transparent;
}
.text-value .scroll-pane {
-fx-background-insets: 0, 1;
-fx-background-radius: 3, 2;
-fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
}
.text-value:focused .scroll-pane {
-fx-background-color:
-fx-focus-color,
-fx-control-inner-background,
-fx-faint-focus-color,
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
-fx-background-insets: -0.2, 1, -1.4, 3;
-fx-background-radius: 3, 2, 4, 0;
}
.text-value > .scroll-pane > .scroll-bar:horizontal {
-fx-background-insets: 0 1 0 1;
-fx-background-radius: 0 0 2 2;
}
.text-value > .scroll-pane > .scroll-bar:vertical {
-fx-background-insets: 1 0 1 0;
-fx-background-radius: 0 2 2 0;
}
.text-value > .scroll-pane > .corner {
-fx-background-insets: 0;
-fx-background-radius: 0 0 2 0;
}
.text-value .content {
-fx-padding: 0.25em 0.583em 0.25em 0.583em; /* 3 7 3 7 */
-fx-cursor: text;
-fx-background-color:
linear-gradient(from 0px 0px to 0px 4px, derive(-fx-control-inner-background, -8%), -fx-control-inner-background);
-fx-background-radius: 2;
}
.text-value:focused .content {
-fx-background-color:
-fx-control-inner-background,
-fx-faint-focus-color,
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
-fx-background-insets: 0, 0, 2;
-fx-background-radius: 2, 1, 0;
}
.text-value > .hint {
-fx-padding: 0.25em 0 0 0;
}
最终 TextValue.class:
public class TextValue extends TextArea {
public TextValue() {
getStyleClass().add("text-value");
}
private StringProperty hint = new SimpleStringProperty(this, "hint");
public ReadOnlyStringProperty hintProperty() {
return hint;
}
public void setHint(String hint) {
this.hint.set(hint);
}
@Override
protected Skin<?> createDefaultSkin() {
return new TextValueSkin(this);
}
}
最终 TextValueSkin.class:
public class TextValueSkin extends TextAreaSkin {
final private TextValue textValue;
private Label lblHint;
public TextValueSkin(final TextValue control) {
super(control);
this.textValue = control;
registerChangeListener(textValue.hintProperty(), e -> updateHint());
}
private void updateHint() {
getChildren().remove(lblHint);
String hint = textValue.hintProperty().get();
if (hint != null && !hint.isEmpty()) {
lblHint = new Label(hint);
lblHint.getStyleClass().add("hint");
lblHint.setManaged(false);
getChildren().add(lblHint);
} else {
lblHint = null;
}
}
@Override
protected void layoutChildren(double x, double y, double w, double h) {
final double bottomHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.prefHeight(w));
final double textHeight = h - snapSizeY(bottomHeight);
super.layoutChildren(x, y, w, textHeight);
if (lblHint != null) {
lblHint.resizeRelocate(x, textHeight, w, bottomHeight);
}
}
@Override
protected double computePrefHeight(double w, double topInset, double rightInset, double bottomInset, double leftInset) {
final double prefHeight = super.computePrefHeight(w, topInset, rightInset, bottomInset, leftInset);
final double addHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.prefHeight(w));
return prefHeight + addHeight;
}
@Override
protected double computeMinHeight(double w, double topInset, double rightInset, double bottomInset, double leftInset) {
final double minHeight = super.computeMinHeight(w, topInset, rightInset, bottomInset, leftInset);
final double addHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.minHeight(w));
return minHeight + addHeight;
}
@Override
protected double computeMaxHeight(double w, double topInset, double rightInset, double bottomInset, double leftInset) {
final double maxHeight = super.computeMaxHeight(w, topInset, rightInset, bottomInset, leftInset);
final double addHeight = lblHint == null ? 0.0 : snapSizeY(lblHint.maxHeight(w));
return maxHeight + addHeight;
}
}
最终演示 Main.class:
public class Main extends Application {
@Override
public void start(Stage stage) {
TextValue textValue = new TextValue();
textValue.setText("Text");
TextField tf = new TextField("");
VBox vBox = new VBox(textValue, tf);
vBox.getStyleClass().add("vbox-demo");
Scene scene = new Scene(vBox);
scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm());
stage.setScene(scene);
stage.show();
textValue.setHint("HINT");
}
}
推荐阅读
- java - 在另一个类错误中使用一个类的对象数组和方法 - JAVA
- html - 具有相同最大宽度的 2 个 div 显示不同的宽度
- android - 无法从 firebase 检索图像
- sqlite - 添加新表 Flutter 后 sqflite 在 Android 上崩溃
- tizen - 在 tizen 可穿戴应用程序中大约一个小时后获取 GPS 的 LOCATIONS_ERROR_SERVICE_NOT_AVAILABLE
- elixir - Ecto(Phoenix)中的外键数组
- javascript - Chart.js 如何从 json 插入值
- javascript - 如何使用 ReactJs 中的键从父组件打开子组件模式
- c++ - 适用于 C++ 的 AWS Cloud9
- javascript - 每次呈现页面时如何更改变量