JavaFX多色背景矩形

JavaFX multicolor background Rectangle

一个矩形可以有两种或两种以上的背景色吗?比如 50% 黑色,50% 红色?

我想制作一个健康栏来显示剩余的健康状况。我以为我可以做类似的东西:

life = 100
width with green color = 99,98,....,0%
width with gray color = 0,1,...,100% (end of game)

根据你的问题陈述,我相信你上面问题的字面答案必须是 "No",你不能用两种不同的颜色填充矩形的背景。它必须是其中之一。

但要实现您问题中隐含的目标,(来自 James_D 的评论)您可以用第一种颜色绘制一个矩形,然后逐渐用第二个矩形覆盖它。或者您可以绘制两个单独的矩形,每个矩形具有不同的颜色,每个矩形的宽度可以一起填满整个条形图。所以一开始,最右边的矩形将 100% 填充条形,而最左边的矩形将填充 0%。然后每个的大小都会改变,右边为 99%,左边为 1%。等等。

JavaFX Rectangle class

有很多不同的方法可以做到这一点。我可能更喜欢使用 ProgressBar 中已经定义的功能,并使用 CSS 对其进行样式设置以获得所需的颜色;或者只使用常规 Pane 并使用两个嵌套的背景颜色并改变插图。下面的演示中显示了这些以及使用矩形等的各种其他选项:

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TwoColoredRectangle extends Application {

    @Override
    public void start(Stage primaryStage) {
        // property to change:
        DoubleProperty life = new SimpleDoubleProperty(100);

        // "animate" the property for demo:
        Timeline timeline = new Timeline(
                new KeyFrame(Duration.ZERO, new KeyValue(life, 100)),
                new KeyFrame(Duration.seconds(2), new KeyValue(life, 0))
        );

        // button to set the animation going:
        Button startButton = new Button("Start");
        startButton.setOnAction(e -> timeline.playFromStart());

        VBox root = new VBox(5);

        // Solution 1. One rectangle on another:
        Rectangle base = new Rectangle(0, 0, 400, 20);
        base.setFill(Color.GRAY);

        Rectangle lifeRect = new Rectangle(0, 0, 400, 20);
        lifeRect.setFill(Color.GREEN);
        lifeRect.widthProperty().bind(life.multiply(4));

        Pane solutionPane1 = new Pane(base, lifeRect);
        root.getChildren().add(createSolutionPane("Dynamic rectangle on fixed rectangle", solutionPane1));

        // Solution 2: Two rectangles changing together:
        Rectangle leftRect = new Rectangle();
        leftRect.setHeight(20);
        leftRect.setFill(Color.GREEN);

        Rectangle rightRect = new Rectangle();
        rightRect.setHeight(20);
        rightRect.setFill(Color.GRAY);

        leftRect.widthProperty().bind(life.multiply(4));
        rightRect.xProperty().bind(leftRect.widthProperty());
        rightRect.widthProperty().bind(life.multiply(-4).add(400));

        Pane solutionPane2 = new Pane(leftRect, rightRect);
        root.getChildren().add(createSolutionPane("Two dynamic rectangles", solutionPane2));

        // Solution 3: Green rectangle on gray-styled pane:
        Pane basePane = new Pane();
        basePane.setMinSize(400, 20);
        basePane.setMaxSize(400, 20);
        // gray color will be defined in CSS:
        basePane.getStyleClass().add("base-pane");

        Rectangle rect = new Rectangle();
        rect.setHeight(20);
        rect.setFill(Color.GREEN);
        rect.widthProperty().bind(life.multiply(4));

        basePane.getChildren().add(rect);
        root.getChildren().add(createSolutionPane("Dynamic rectangle on pane", basePane));

        // Solution 4: Dynamically-styled pane:
        Pane dynamicPane = new Pane();
        dynamicPane.setMinSize(400, 20);
        dynamicPane.setMaxSize(400, 20);
        dynamicPane.getStyleClass().add("dynamic-pane");
        // make background insets depend on the property:
        dynamicPane.styleProperty().bind(life.multiply(4).asString("-fx-background-insets: 0, 0 0 0 %f"));

        root.getChildren().add(createSolutionPane("Dynamically styled pane", dynamicPane));

        // Solution 5: Progress bar:
        ProgressBar progressBar = new ProgressBar();
        progressBar.setPrefSize(400, 20);
        progressBar.progressProperty().bind(life.divide(100));
        root.getChildren().add(createSolutionPane("Progress bar", progressBar));


        root.getChildren().add(startButton);

        Scene scene = new Scene(root);
        scene.getStylesheets().add("two-colored-rectangle.css");

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Node createSolutionPane(String title, Node content) {
        VBox vbox = new VBox(new Label(title), content);
        vbox.getStyleClass().add("solution-pane");
        return vbox ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

和样式表 (two-colored-rectangle.css):

.root {
    -fx-padding: 20 ;
    -fx-alignment: center ;
}

.base-pane {
    -fx-background-color: gray ;
}

.dynamic-pane {
    -fx-background-color: green, gray ;
}

.solution-pane {
    -fx-spacing: 5 ;
    -fx-padding: 10 ;
}

.progress-bar {
    -fx-accent: green ;
    -fx-control-inner-background: gray ;
}

这可以使用 LinearGradient 作为 background 的填充来完成:

Region rect = new Region();
rect.setMaxSize(100, 20);
rect.setPrefSize(100, 20);

代码

rect.setBackground(new Background(new BackgroundFill(new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE,
    new Stop(0.3, Color.GREEN),
    new Stop(0.3, Color.RED)
), CornerRadii.EMPTY, Insets.EMPTY)));

CSS

或者您也可以使用 CSS:

rect.setId("myrect");
#myrect {
    -fx-background-color: linear-gradient(to right, green 30%, red 30%);
}

两个版本都用绿色填充 Rectangle 的前 30%,用红色填充其余部分(水平)。

请注意,James_D的解决方案应该是首选。这只是为了完整起见。

LinearGradient extends Paint 因此您也可以将它分配给 Rectanglefill 属性(或任何其他 Shape).