英文:
JavaFX how to force Label to update its prefWidth after changing the text?
问题
只需将我的Label
更新为在文本更改时适应其文本的prefWidth
。
首先,您可以将以下代码添加到Label
以使其根据文本内容自动更新prefWidth
:
label.textProperty().addListener((observable, oldValue, newValue) -> {
label.setPrefWidth(label.prefWidth(-1)); // Use -1 to compute the preferred width based on the current text
});
在您提供的代码中,将这段代码添加到Label
创建后的位置,如下所示:
Label label = new Label();
// ...
box.prefWidthProperty().bind(label.prefWidthProperty().add(button.prefWidthProperty()));
// Add the text change listener here
label.textProperty().addListener((observable, oldValue, newValue) -> {
label.setPrefWidth(label.prefWidth(-1));
});
这样,每当Label
的文本更改时,它都会自动调整其prefWidth
以适应文本内容。
英文:
Pretty simple thing actually: I want my Label
to update its prefWidth
to fit the text into itself whenever the text changes.
Initially the Label
s text is null
and the prefWidth
is -1.0
without setting it manually. So when the Label
s text changes at runtime the prefWidth is still -1.0
and the Label
displays "..." instead of the set text, because it is to small.
The Label
is inside an HBox
. That HBox
s prefWidth
is bound to the sum of its childrens prefWidth
s and the HBox
has enough space to grow.
Here's some code to reproduce:
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
box.prefWidthProperty().bind(label.prefWidthProperty().add(button.prefWidthProperty()));
window.setScene(new Scene(new StackPane(box)));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
How can I get the Label
to update its width to fit its text?
EDIT: The root of the Scene is a StackPane
not a Pane
.
答案1
得分: 2
不要使用绑定来尝试在窗格的最小/首选/最大大小上执行布局是不明智的。HBox
已经知道如何计算其首选宽度,并且默认情况下会按照您想要的方式执行(简单版本是将其计算为其子节点的首选宽度之和,尽管实际上更复杂,因为它考虑了间距、填充等)。
控件和布局窗格的prefWidth
的默认值都是特殊值Region.USE_COMPUTED_SIZE
,这只是一个标志,告诉节点计算其首选大小以向布局它的人报告。标签将通过检查显示其文本(和图形)所需的大小来计算其首选大小,按钮也是如此,布局窗格将检查其子节点的首选大小。Region.USE_COMPUTED_SIZE
的实际值为-1.0
,这就是为什么每次调用getPrefWidth()
时您总是看到该值的原因。
HBox
被扩展以填充堆栈窗格的原因是这正是StackPane
的行为。从文档中可以看到:
堆栈窗格将尝试调整每个子节点以填充其内容区域。
您可以通过使用不具有该行为的不同根窗格来防止这种情况发生(例如,一个普通的Pane
或另一个HBox
等):
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
window.setScene(new Scene(new HBox(box)));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
或者通过将您的HBox
包装在另一个窗格中(这样包装它的窗格将填充StackPane
,但您的box
不会填充其父窗格):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
window.setScene(new Scene(new StackPane(new HBox(box))));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
或者通过强制box
不超过其首选大小来防止其增长,通过将其maxWidth
设置为使用首选大小:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
box.setMaxWidth(Region.USE_PREF_SIZE);
window.setScene(new Scene(new StackPane(box)));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
您没有在问题中指示为什么要在StackPane
的父节点中使用HBox
。由于StackPane
的唯一功能是按z顺序叠加其子节点,如果您正在使用堆栈窗格,那么您可能正在在其上方或下方添加其他节点。因此,可能根本不需要防止HBox
的大小调整(因为它不会影响叠加),但也许只需要确保HBox
的对齐属性(以及StackPane
的其他子节点)设置为与StackPane
相同(默认为居中)。也就是说,您可能只需要box.setAlignment(Pos.CENTER)
。
英文:
It's never a good idea to try to perform layout using bindings on the min/pref/max size of a pane. The HBox
already knows how to compute its preferred width, and by default does exactly what you want (the simple version is it computes it as the sum of the preferred widths of its child nodes, though in reality it's more complex, as it accounts for spacing, padding, etc.).
The default value for prefWidth
for both controls and layout panes is the sentinel value Region.USE_COMPUTED_SIZE
, which is just a flag to tell the node to compute its preferred size for reporting to whoever is laying it out. A label will compute its preferred size by checking the size required to display its text (and graphic), similarly for a button, and layout panes will examine the preferred sizes of their child nodes. The actual value of Region.USE_COMPUTED_SIZE
is -1.0
, which is why you always see that value when you call getPrefWidth()
.
The reason the HBox
is being expanded to fill the stack pane is because that's exactly the behavior of a StackPane
. From the docs:
> The stackpane will attempt to resize each child to fill its content area.
You can prevent this either by using a different root pane, which doesn't have that behavior, (e.g. a plain Pane
or another HBox
, etc.):
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
window.setScene(new Scene(new HBox(box)));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
or by wrapping your HBox
in another pane (so the pane in which it is wrapped will fill the StackPane
, but your box
won't fill its parent):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
window.setScene(new Scene(new StackPane(new HBox(box))));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
or by forcing the box
not to grow beyond its preferred size, by setting its maxWidth
to use the preferred size:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Spielwiese extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
Label label = new Label();
Button button = new Button("Fill Label");
button.setOnAction(e -> label.setText("Some Text"));
HBox box = new HBox(label, button);
box.setMaxWidth(Region.USE_PREF_SIZE);
window.setScene(new Scene(new StackPane(box)));
window.setWidth(800);
window.setHeight(600);
window.show();
}
}
You didn't indicate in your question why you wanted to use a StackPane
for the parent of the HBox
. Since the sole functionality of a StackPane
is to stack its child nodes on top of each other in z-order, if you're using a stack pane you're presumably adding other nodes on top of, or behind it. Consequently, it might actually not be necessary at all to prevent the HBox
resizing (because it won't affect the stacking), but maybe it's enough just to ensure the alignment property of the HBox
(and other child nodes of the StackPane
) is set to the same as the StackPane
(which is centered, by default). I.e. you might just need box.setAlignment(Pos.CENTER)
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论