【问题标题】:How to center text without using any layout?如何在不使用任何布局的情况下使文本居中?
【发布时间】:2020-10-17 13:33:25
【问题描述】:
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。这就是问题所在。我永远无法提前知道必须从水平位置中扣除多少。

请问我该如何解决?

谢谢

【问题讨论】:

  • 你可以使用Label吗?
  • 哦,我没想到。当然是的。但是,它会有所帮助还是结果会一样?我不确定。
  • 由于标签具有文本对齐属性,我希望您仍然需要做一些工作。我会尝试将标签的最小宽度绑定到包含窗格的宽度。
  • 使用布局管理器。如果你不这样做,你的老师就不会正确地展示材料。
  • 我认为您的要求没有意义。根据定义,居中是“布局”。 JavaFX 中支持的布局方式是使用某种Pane(您正在这样做):使用现有的Pane 子类,或者编写自己的Parent 子类(或RegionPane)并覆盖适当的方法(最重要的是layoutChildren())。但真正的Label 似乎有你需要的功能。

标签: java javafx layout position text-alignment


【解决方案1】:

这一切都可以通过使用StackPane(例如@Oboe's answer 所示)更轻松地完成。使用适当的布局应该始终是您的首选。但是,我的其余答案显示了如何手动完成。


xy 属性定义了文本节点的原点。 x 坐标确定文本左侧的开始位置。但是,可以通过 textOrigin 属性在一定程度上自定义 y 坐标。

                   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 属性会更改文本的布局边界,上面会导致StackOverflowError。文本的本地边界 (this is a characteristic of shapes) 也是如此。如果您不介意通过其 layoutXlayoutY 属性定位文本,则有一个解决方案:

// 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));

注意layoutXlayoutY 属性来自Node。布局使用它们来定位它们的子级。但是由于您没有使用自动定位其子级的布局,您可以安全地手动设置它们。还有translateXtranslateY 属性。这些添加到布局属性中。例如,ignoring other things,最终的 x 位置将为layoutX + translateX(与最终的 y 位置类似);对于 Text 节点,最终的 x 位置将是 x + layoutX + translateX(同样,最终的 y 位置类似)。

您可能已经注意到,我的所有示例都警告说,假设矩形位于其父级的原点。要解决这个问题,您只需将矩形 x 和 y 位置添加到文本的布局位置。

【讨论】:

  • 感谢您的解释。是完美的。当然这个只能用于位置(0,0),否则必须调整。因此,我想补充一点,如果位置是例如(100,100),对于 LayoutX,添加 100 - text.layoutXProperty().bind(Bindings.createDoubleBinding(() -> 100 + ... 就足够了,并且必须以与 LayoutX 类似的形式重写 LayoutY - text.layoutYProperty().bind(Bindings.createDoubleBinding(() -> 100 + rectangle.getHeight() / 2, rectangle.heightProperty(), text.layoutBoundsProperty()));
  • 您好,我还有一个问题。关键是我想再次使用代码,但是这次将文本对齐到 [0,0] 并且我无法摆脱这个奇怪的缩进,正如我在问题中的图片中所说明的那样。请问如何调整它以使其正常工作?谢谢
  • 也许可以试试bounds type
【解决方案2】:

您可以使用 Label 及其 alignment 来代替 Text 对象来使文本居中。然后你只需要强制 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();
}

【讨论】:

  • 谢谢。不幸的是,垂直对齐不起作用。我尝试添加text.minHeightProperty().bind(pane.heightProperty());text.setPadding(new Insets(0));,但没有效果。请问有什么问题?
  • 不是吗?无论是否设置插图,它似乎都对我有用。文本是否总是顶部对齐?
  • 不幸的是,没有。 setAlignment 仅对水平对齐有影响。文本始终垂直居中,但窗口的高度不是 100,而是 185。
  • 你是说垂直居中在视觉上看起来是正确的,但数字似乎不正确?
  • 是的,文本垂直居中。但是,不是在矩形的中心,而是在窗口的中心,高度为 185。虽然,这可能是因为文本的高度为 146。
【解决方案3】:

如果不强制使用那些特定的Nodes,我会使用Label 而不是TextStackPane 而不是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。然后你会得到两个Rectangles,一个是透明的文本,另一个是背景(这会给文本着色)。

这些方法的好处是您以两个具有相同边界的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);
    }
}

【讨论】:

  • 很抱歉,但这是我想避免的。正如我在问题标题中所写,我不想使用任何布局。当然,我必须使用Pane,我一直使用它,但我想在不使用StackPane等的情况下实现它。这只是一个简单的例子。
  • @yoda666,在这种情况下,我会建议一个完全开箱即用的解决方案。您可以创建一个新的Shape,从Rectangle 中减去Text。然后您将拥有 2 个Rectangles,一个具有透明文本,另一个用于背景(这将为文本着色)。我更新了答案以包含这种方法。
  • 对不起,我不明白您为什么使用Shape?当(在您的代码中)我将Rectangle rectangle = new Rectangle(600, 100); 更改为Rectangle rectangle = new Rectangle(600, 100, Color.TURQUOISE); 并将stage.setScene(new Scene(new Pane(rectangle, shape))); 更改为stage.setScene(new Scene(new Pane(rectangle, text)));。而我删除了Shape shape = Shape.subtract(rectangle, text);shape.setFill(Color.TURQUOISE);,结果还是一样的。
  • @yoda666,视觉效果是一样的。但是,正如我在答案中解释的那样,不同之处在于新的Shape 与矩形具有完全相同的Bounds,而Text 没有。所以现在你没有将Text 定位在Rectangle 的中心。相反,您只需将RectangleShape 定位在同一位置,或者您可以绑定位置(例如,您可以绑定layoutXlayoutYtranslateXtranslateY 等) .
猜你喜欢
  • 1970-01-01
  • 2018-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
相关资源
最近更新 更多