JavaFX:设置Text控件的背景颜色

7
我正在使用TextFlow和一些Text项来展示样式化文本,但是我无法找到一种设置Text项简单背景颜色的方法。
我可以设置填充颜色和字体,但它没有一个Java方法或CSS属性来设置其背景颜色。
2个回答

8

基于这个解决方案,这是一个快速实现的方法,为FlowPane中所有Text节点提供背景颜色,使用CSS和设置一系列分隔符为逗号(与Text项一样多)和每个元素的插入:

private FlowPane flow;
private Scene scene;

@Override
public void start(Stage primaryStage) {
    Text text0 = new Text("These are several ");
    Text text1 = new Text("Text Nodes ");
    Text text2 = new Text("wrapped in ");
    Text text3 = new Text("a FlowPane");
    text0.setFill(Color.WHEAT);
    text0.setFont(new Font("Times New Roman", 20));
    text1.setFill(Color.WHITE);
    text1.setFont(new Font("Verdana", 32));
    text2.setFill(Color.WHITESMOKE);
    text2.setFont(new Font("Arial", 24));
    text3.setFill(Color.WHITESMOKE);
    text3.setFont(new Font("Arial", 18));

    flow = new FlowPane(text0, text1, text2, text3);
    scene = new Scene(flow, 300, 200);

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();

    setBackgroundColors();
    flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());        
}

private void setBackgroundColors(){
    final Bounds out = flow.getBoundsInLocal();
    final StringBuilder sbColors = new StringBuilder();
    final StringBuilder sbInsets = new StringBuilder();
    AtomicInteger cont = new AtomicInteger();
    flow.getChildrenUnmodifiable().forEach(n->{
        sbColors.append("hsb(")
                .append((((double)cont.get())/((double)flow.getChildren().size()))*360d)
                .append(", 60%, 90%)");
        Bounds b = ((Text)n).getBoundsInParent();
        sbInsets.append(b.getMinY()).append(" ");
        sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()).append(" ");
        sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
        sbInsets.append(b.getMinX());
        if(cont.getAndIncrement()<flow.getChildren().size()-1){
            sbColors.append(", ");
            sbInsets.append(", ");
        }
    });
    flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}

这将导致以下结果: Flow1 在调整场景大小后: Flow2 编辑:
基于OP的要求,使用TextFlow布局而不是FlowPane。由于Text节点可以跨越TextFlow中的多行,所以给定的解决方案将不再有效,因为每个文本节点的边界框会重叠其他节点。
作为解决方法,我们可以将Text节点拆分为单词Text节点,同时保持相同原始短语的背景颜色。
我不会进入拆分逻辑,但我会添加一个索引列表,其中每个索引将文本节点映射到其背景颜色的索引。
private FlowPane flow;
private Scene scene;

private final List<Integer> indices=Arrays.asList(0,0,0,1,1,2,2,3,3);

@Override
public void start(Stage primaryStage) {
    List<Text> text0 = Arrays.asList(new Text("These "), new Text("are "), new Text("several "));
    List<Text> text1 = Arrays.asList(new Text("Text "), new Text("Nodes "));
    List<Text> text2 = Arrays.asList(new Text("wrapped "), new Text("in "));
    List<Text> text3 = Arrays.asList(new Text("a "), new Text("FlowPane"));
    text0.forEach(t->t.setFill(Color.WHEAT));
    text0.forEach(t->t.setFont(new Font("Times New Roman", 20)));
    text1.forEach(t->t.setFill(Color.WHITE));
    text1.forEach(t->t.setFont(new Font("Verdana", 32)));
    text2.forEach(t->t.setFill(Color.WHITESMOKE));
    text2.forEach(t->t.setFont(new Font("Arial", 24)));
    text3.forEach(t->t.setFill(Color.WHITESMOKE));
    text3.forEach(t->t.setFont(new Font("Arial", 18)));

    flow = new FlowPane();
    flow.getChildren().addAll(text0);
    flow.getChildren().addAll(text1);
    flow.getChildren().addAll(text2);
    flow.getChildren().addAll(text3);
    scene = new Scene(flow, 300, 200);

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();

    setBackgroundColors();
    flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());        
}

private void setBackgroundColors(){
    final Bounds out = flow.getBoundsInLocal();
    final StringBuilder sbColors = new StringBuilder();
    final StringBuilder sbInsets = new StringBuilder();
    AtomicInteger cont = new AtomicInteger();
    flow.getChildrenUnmodifiable().forEach(n->{
        sbColors.append("hsb(")
                .append((double)indices.get(cont.get())/(double)(indices.get(flow.getChildren().size()-1)+1)*360d)
                .append(", 60%, 90%)");
        Bounds b = ((Text)n).getBoundsInParent();
        sbInsets.append(b.getMinY()).append(" ");
        sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()-1).append(" ");
        sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
        sbInsets.append(b.getMinX());
        if(cont.getAndIncrement()<flow.getChildren().size()-1){
            sbColors.append(", ");
            sbInsets.append(", ");
        }
    });
    flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}

现在,这个 FlowPane 表现得像一个 TextFlow

Flow3


不错的解决方法,谢谢!但是当输入文本逐渐到来时,速度会变得非常慢。在我的情况下,它是从MUD输出的,这实际上是带有ANSI颜色的telnet协议。 - Farzad Bekran
好吧,这是一个解决方法... 你有多少个“Text”节点? - José Pereda
不完全是我想要的,因为您使用了FlowPane,它不会自动换行长行,当我将其更改为TextFlow时,颜色混乱了。 - Farzad Bekran
你可以依靠成千上万的节点,不过我改变了主意,我会使用 WebView 并逐步添加 HTML 节点。虽然它使用 JavaScript 并且必须在 JavaFX 线程中完成,速度不是很快,但仍然比使用大量 Text 对象的 TextFlow 更好。 - Farzad Bekran
确实,这正是我所期望的!但我应该小心我所期望的 :) 谢谢您的努力,先生! - Farzad Bekran
显示剩余2条评论

4

文本对象没有背景。您可以将其与形状(矩形、椭圆等)分组并设置该形状的颜色,或者将对象放置在StackPane中并设置StackPane的背景颜色。


没有问题,我对TextFlow很满意,问题在于Text对象。 - Farzad Bekran
问题询问如何设置“Text”对象的背景,而不是“TextFlow”对象的背景。 - James_D
哎呀,你说得对。我看错了问题。我的错!我更新并修正了我的答案,这次回答了正确的问题! - Ra'kiir
这并不像预期的那样工作,TextFlow将StackPanes视为单独的对象,而不是像Text对象一样会被TextFlow自动换行。 - Farzad Bekran
在我的应用程序中,我只需要突出显示单个短词,因此StackPane是最简单的解决方案。但我发现TextFlow会在突出显示单词的行后添加额外的行间距。经过多次尝试,我发现对Text对象执行setLayoutY(10)不会影响文本的位置,但可以防止额外的行间距。我不知道为什么 :) - John Christofolakos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接