有很多方法可以解决这个问题。我提出了三个示例解决方案:
- 使用固定宽度渐变。
- 使用剪辑。
- 使用插值器。
所有提出的解决方案都采用了固定大小的简单方法。如果您想要一个动态大小,您可以在底层矩形宽度和高度上使用适当的侦听器或绑定来实现它,以更新相关的剪辑或渐变设置。
使用固定宽度渐变
渐变是在固定坐标而不是比例坐标中定义的(停靠点仍然是比例的)。矩形本身的宽度是总宽度的百分比,线性渐变是为整个宽度定义的。任何不适合矩形的渐变部分都不会显示,从而给出所需的结果。
LinearGradient gradient = new LinearGradient(
0, 0, W, 0,
false,
CycleMethod.NO_CYCLE,
new Stop(0, Color.LAWNGREEN),
new Stop(.5, Color.YELLOW),
new Stop(1, Color.ORANGERED)
);
return new Rectangle(W * visiblePortion, H, gradient);
使用剪辑
在这里,我们将剪辑应用于具有渐变的矩形,因此只有矩形的一部分是可见的。
Rectangle rect = new Rectangle(W, H);
rect.setStyle("-fx-fill: linear-gradient(to right, lawngreen, yellow, orangered);");
Rectangle clip = new Rectangle(W * visiblePortion, H);
rect.setClip(clip);
该示例使用 css 定义渐变,但如果您愿意,您可以使用 LinearGradient API 在 Java 代码中定义它。
使用插值器
如果您想在没有所需样式的剪辑的情况下进行比例渐变,则需要调整渐变中使用的颜色和色标以匹配所需的百分比显示(使用您为计算创建的算法)。
双色平滑渐变很简单,但一般的解决方案很困难,因为具有多个停止的渐变定义变得复杂。我这里只提供一种双色平滑渐变的解决方案。
该实现使用Color.interpolate() 函数(作为 Color 实现 Interpolatable)来计算渐变的停止颜色。
图像看起来与基于剪辑的解决方案不同,因为基于剪辑的解决方案使用了更复杂的 3 级渐变,而该解决方案仅使用了简单的两种颜色渐变。
这个实现的关键是使用插值器定义梯度:
LinearGradient gradient = new LinearGradient(
0, 0, 1, 0,
true,
CycleMethod.NO_CYCLE,
new Stop(0, Color.GREEN),
new Stop(1, Color.GREEN.interpolate(Color.RED, visiblePortion))
);
return new Rectangle(W * visiblePortion, H, gradient);
使用进度条
图像看起来很像一个进度条。如果合适,您可以调查styling a progress bar。我不会在这里为渐变样式的进度条提供解决方案。如果这是您想要的,请专门提出一个新问题。
示例应用程序
import javafx.application.Application;
import javafx.geometry.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.function.Function;
public class GradientDemo extends Application {
private static final double W = 500;
private static final double H = 10;
private static final double STEP = 0.2;
@Override
public void start(Stage stage) {
final VBox layout = new VBox(10);
layout.setAlignment(Pos.TOP_LEFT);
layout.setPadding(new Insets(10));
addGradients(layout, "fixedWidthTriColorGradient", this::fixedWidthTriColorGradient);
addGradients(layout, "clippedTriColorGradient", this::clippedTriColorGradient);
addGradients(layout, "interpolatedTwoColorGradient", this::interpolatedTwoColorGradient);
stage.setScene(new Scene(layout));
stage.show();
}
private void addGradients(
VBox layout,
String gradientName,
Function<Double, Node> gradientFactory
) {
Label label = new Label(gradientName);
label.setPadding(new Insets(5, 0, 0, 0));
layout.getChildren().addAll(
label
);
for (double f = STEP; f <= 1.0; f += STEP) {
layout.getChildren().addAll(gradientFactory.apply(f));
}
}
public Rectangle fixedWidthTriColorGradient(double visiblePortion) {
LinearGradient gradient = new LinearGradient(
0, 0, W, 0,
false,
CycleMethod.NO_CYCLE,
new Stop(0, Color.LAWNGREEN),
new Stop(.5, Color.YELLOW),
new Stop(1, Color.ORANGERED)
);
return new Rectangle(W * visiblePortion, H, gradient);
}
public Rectangle clippedTriColorGradient(double visiblePortion) {
Rectangle rect = new Rectangle(W, H);
rect.setStyle("-fx-fill: linear-gradient(to right, lawngreen, yellow, orangered);");
Rectangle clip = new Rectangle(W * visiblePortion, H);
rect.setClip(clip);
return rect;
}
public Rectangle interpolatedTwoColorGradient(double visiblePortion) {
LinearGradient gradient = new LinearGradient(
0, 0, 1, 0,
true,
CycleMethod.NO_CYCLE,
new Stop(0, Color.GREEN),
new Stop(1, Color.GREEN.interpolate(Color.RED, visiblePortion))
);
return new Rectangle(W * visiblePortion, H, gradient);
}
public static void main(String[] args) {
launch(args);
}
}