如何在不使用任何布局的情况下使文本居中?
How to center text without using any layout?
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class HorizontalTextAlignment extends javafx.application.Application {
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Text(rectangle.getWidth() / 2, rectangle.getHeight() / 2, "TEXT");
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
我需要在不使用任何布局的情况下将文本居中。您可能会注意到,虽然垂直对齐有效,但水平对齐无效。当然,这可以通过简单的计算解决。
// You need to change the horizontal position of the text for this to work.
var text = new Text(rectangle.getLayoutX(), rectangle.getHeight() / 2, "TEXT");
...
var rectangleWidth = rectangle.getWidth();
var textWidth = text.getLayoutBounds().getWidth();
text.setLayoutX((rectangleWidth - textWidth) / 2);
问题是,虽然在图片中不太容易看出它不在正中间
如果只更改初始化,这个很好看。
var text = new Text(rectangle.getLayoutX(), rectangle.getHeight() / 2, "TEXT");
但是,这不能通过简单地从水平位置减去所需的值(在本例中为 -2)来解决,因为当我更改文本时。
需要减去9,这就是问题所在。我永远无法提前知道必须从水平位置扣除多少。
请问我该如何解决?
谢谢
如果不是被迫使用那些特定的 Node
s,我会使用 Label
而不是 Text
和 StackPane
而不是 Pane
:
Rectangle rectangle = new Rectangle(600, 100, Color.TURQUOISE);
Label label = new Label("TEXT");
label.setAlignment(Pos.CENTER);
label.setFont(new Font(100));
StackPane.setAlignment(label, Pos.CENTER);
StackPane.setAlignment(rectangle, Pos.CENTER);
StackPane pane = new StackPane(rectangle, label);
pane.prefWidthProperty().bind(rectangle.widthProperty());
pane.prefHeightProperty().bind(rectangle.heightProperty());
stage.setScene(new Scene(pane));
stage.show();
更新
鉴于您不能使用 StackPane,这里有一个完全开箱即用的解决方案。
您可以创建一个新的 Shape
从 Rectangle
中减去 Text
。然后你将有两个 Rectangle
s,一个文本透明,另一个用于背景(这将为文本提供颜色)。
这些方法的好处是您以两个具有相同边界的 Shapes
结尾,并且更容易在 Scene
.
中进行布局
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
Rectangle rectangle = new Rectangle(600, 100);
Text text = new Text("TEXT");
text.setTextAlignment(TextAlignment.CENTER);
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextOrigin(VPos.CENTER);
text.setX(rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2);
text.setY(rectangle.getHeight() / 2);
Shape shape = Shape.subtract(rectangle, text);
shape.setFill(Color.TURQUOISE);
shape.layoutXProperty().bind(rectangle.layoutXProperty());
shape.layoutYProperty().bind(rectangle.layoutYProperty());
shape.translateXProperty().bind(rectangle.translateXProperty());
shape.translateYProperty().bind(rectangle.translateYProperty());
stage.setScene(new Scene(new Pane(rectangle, shape)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
使用 StackPane
(例如 中所示)可以更轻松地完成所有这些操作。使用适当的布局应该始终是您的第一选择。但是,我的其余答案显示了如何手动完成。
x
和 y
属性定义文本节点的 原点 。 x-coordinate 确定文本左侧的起始位置。但是,y-coordinate 可以通过 textOrigin
属性.
进行一些自定义
X (always)
|
Y (VPos.TOP)-------╔════════════════════════════════╗
║ ║
Y (VPos.CENTER)----║ Text Node ║
║ ║
Y (VPos.BOTTOM)----╚════════════════════════════════╝
注意:还有 VPos.BASELINE
(默认值),但我不知道如何形象化。
如您所见,当您将 x
属性 设置为 rectangle.getWidth() / 2
时,您正在对齐文本节点的 左侧 与矩形的水平中心。如果要将文本节点的水平中心与矩形的水平中心对齐,则必须考虑文本节点的宽度。这是一个例子:
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Text("TEXT");
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
// WARNING: Assumes Rectangle is at (0, 0)
text.setX(rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2);
text.setY(rectangle.getHeight() / 2);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
}
请注意,我在设置其他所有内容(例如字体、文本等)后设置了 x
属性。这是已知结果文本宽度所必需的。当然,这不是响应式的;如果矩形或文本的尺寸发生变化,您必须手动重新计算新的 x
值。当我们忽略布局时,理想的解决方案是使用绑定。类似于:
// WARNING: Assumes Rectangle is at (0, 0)
text.xProperty()
.bind(
Bindings.createDoubleBinding(
() -> rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2,
rectangle.widthProperty(),
text.layoutBoundsProperty()));
不幸的是,更改 x
属性 会更改文本的布局边界,以上会导致 WhosebugError
。文本的bounds-in-local(this is a characteristic of shapes)也是如此。如果您不介意通过其 layoutX
和 layoutY
属性定位文本,则有一个解决方案:
// WARNING: Assumes Rectangle is at (0, 0)
// leave text.x = 0 and text.y = 0
text.layoutXProperty()
.bind(
Bindings.createDoubleBinding(
() -> rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2,
rectangle.widthProperty(),
text.layoutBoundsProperty()));
// assumes textOrigin = VPos.CENTER
text.layoutYProperty().bind(rectangle.heightProperty().divide(2));
请注意 layoutX
和 layoutY
属性来自 Node
。布局使用它们来定位它们的 children。但是由于您没有使用自动定位其 children 的布局,您可以安全地手动设置它们。还有 translateX
和 translateY
属性。这些添加到布局属性中。例如,ignoring other things,最终的x-position将是layoutX + translateX
(与最终的y-position类似);对于 Text
节点,最终的 x-position 将是 x + layoutX + translateX
(同样,对于最终的 y-position)。
您可能已经注意到,我的所有示例都警告不要假定矩形位于其 parent 的原点。要解决该问题,您只需将矩形 x 和 y 位置添加到文本的布局位置即可。
您可以使用 Label with its alignment 集来使文本居中,而不是文本对象。然后你只需要强制 Label 的最小宽度与父级的宽度相匹配(这大致是模仿某些布局所做的):
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Label("TEXT");
text.setFont(new Font(100));
text.setAlignment(Pos.CENTER);
Pane pane = new Pane(rectangle, text);
text.minWidthProperty().bind(pane.widthProperty());
text.minHeightProperty().bind(pane.heightProperty());
stage.setScene(new Scene(pane));
stage.show();
}
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class HorizontalTextAlignment extends javafx.application.Application {
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Text(rectangle.getWidth() / 2, rectangle.getHeight() / 2, "TEXT");
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
我需要在不使用任何布局的情况下将文本居中。您可能会注意到,虽然垂直对齐有效,但水平对齐无效。当然,这可以通过简单的计算解决。
// You need to change the horizontal position of the text for this to work.
var text = new Text(rectangle.getLayoutX(), rectangle.getHeight() / 2, "TEXT");
...
var rectangleWidth = rectangle.getWidth();
var textWidth = text.getLayoutBounds().getWidth();
text.setLayoutX((rectangleWidth - textWidth) / 2);
问题是,虽然在图片中不太容易看出它不在正中间
如果只更改初始化,这个很好看。
var text = new Text(rectangle.getLayoutX(), rectangle.getHeight() / 2, "TEXT");
但是,这不能通过简单地从水平位置减去所需的值(在本例中为 -2)来解决,因为当我更改文本时。
需要减去9,这就是问题所在。我永远无法提前知道必须从水平位置扣除多少。
请问我该如何解决?
谢谢
如果不是被迫使用那些特定的 Node
s,我会使用 Label
而不是 Text
和 StackPane
而不是 Pane
:
Rectangle rectangle = new Rectangle(600, 100, Color.TURQUOISE);
Label label = new Label("TEXT");
label.setAlignment(Pos.CENTER);
label.setFont(new Font(100));
StackPane.setAlignment(label, Pos.CENTER);
StackPane.setAlignment(rectangle, Pos.CENTER);
StackPane pane = new StackPane(rectangle, label);
pane.prefWidthProperty().bind(rectangle.widthProperty());
pane.prefHeightProperty().bind(rectangle.heightProperty());
stage.setScene(new Scene(pane));
stage.show();
更新
鉴于您不能使用 StackPane,这里有一个完全开箱即用的解决方案。
您可以创建一个新的 Shape
从 Rectangle
中减去 Text
。然后你将有两个 Rectangle
s,一个文本透明,另一个用于背景(这将为文本提供颜色)。
这些方法的好处是您以两个具有相同边界的 Shapes
结尾,并且更容易在 Scene
.
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
Rectangle rectangle = new Rectangle(600, 100);
Text text = new Text("TEXT");
text.setTextAlignment(TextAlignment.CENTER);
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextOrigin(VPos.CENTER);
text.setX(rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2);
text.setY(rectangle.getHeight() / 2);
Shape shape = Shape.subtract(rectangle, text);
shape.setFill(Color.TURQUOISE);
shape.layoutXProperty().bind(rectangle.layoutXProperty());
shape.layoutYProperty().bind(rectangle.layoutYProperty());
shape.translateXProperty().bind(rectangle.translateXProperty());
shape.translateYProperty().bind(rectangle.translateYProperty());
stage.setScene(new Scene(new Pane(rectangle, shape)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
使用 StackPane
(例如
x
和 y
属性定义文本节点的 原点 。 x-coordinate 确定文本左侧的起始位置。但是,y-coordinate 可以通过 textOrigin
属性.
X (always)
|
Y (VPos.TOP)-------╔════════════════════════════════╗
║ ║
Y (VPos.CENTER)----║ Text Node ║
║ ║
Y (VPos.BOTTOM)----╚════════════════════════════════╝
注意:还有 VPos.BASELINE
(默认值),但我不知道如何形象化。
如您所见,当您将 x
属性 设置为 rectangle.getWidth() / 2
时,您正在对齐文本节点的 左侧 与矩形的水平中心。如果要将文本节点的水平中心与矩形的水平中心对齐,则必须考虑文本节点的宽度。这是一个例子:
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Text("TEXT");
text.setBoundsType(TextBoundsType.VISUAL);
text.setFont(new Font(100));
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
// WARNING: Assumes Rectangle is at (0, 0)
text.setX(rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2);
text.setY(rectangle.getHeight() / 2);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
}
请注意,我在设置其他所有内容(例如字体、文本等)后设置了 x
属性。这是已知结果文本宽度所必需的。当然,这不是响应式的;如果矩形或文本的尺寸发生变化,您必须手动重新计算新的 x
值。当我们忽略布局时,理想的解决方案是使用绑定。类似于:
// WARNING: Assumes Rectangle is at (0, 0)
text.xProperty()
.bind(
Bindings.createDoubleBinding(
() -> rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2,
rectangle.widthProperty(),
text.layoutBoundsProperty()));
不幸的是,更改 x
属性 会更改文本的布局边界,以上会导致 WhosebugError
。文本的bounds-in-local(this is a characteristic of shapes)也是如此。如果您不介意通过其 layoutX
和 layoutY
属性定位文本,则有一个解决方案:
// WARNING: Assumes Rectangle is at (0, 0)
// leave text.x = 0 and text.y = 0
text.layoutXProperty()
.bind(
Bindings.createDoubleBinding(
() -> rectangle.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2,
rectangle.widthProperty(),
text.layoutBoundsProperty()));
// assumes textOrigin = VPos.CENTER
text.layoutYProperty().bind(rectangle.heightProperty().divide(2));
请注意 layoutX
和 layoutY
属性来自 Node
。布局使用它们来定位它们的 children。但是由于您没有使用自动定位其 children 的布局,您可以安全地手动设置它们。还有 translateX
和 translateY
属性。这些添加到布局属性中。例如,ignoring other things,最终的x-position将是layoutX + translateX
(与最终的y-position类似);对于 Text
节点,最终的 x-position 将是 x + layoutX + translateX
(同样,对于最终的 y-position)。
您可能已经注意到,我的所有示例都警告不要假定矩形位于其 parent 的原点。要解决该问题,您只需将矩形 x 和 y 位置添加到文本的布局位置即可。
您可以使用 Label with its alignment 集来使文本居中,而不是文本对象。然后你只需要强制 Label 的最小宽度与父级的宽度相匹配(这大致是模仿某些布局所做的):
@Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.TURQUOISE);
var text = new Label("TEXT");
text.setFont(new Font(100));
text.setAlignment(Pos.CENTER);
Pane pane = new Pane(rectangle, text);
text.minWidthProperty().bind(pane.widthProperty());
text.minHeightProperty().bind(pane.heightProperty());
stage.setScene(new Scene(pane));
stage.show();
}