带有阴影的 JavaFX 分层布局

JavaFX layered layouts with shadows

我正在学习 JavaFX,我正在尝试创建一个 AnchorPane,它本身包含另外 3 个 AnchorPane。目前,我有一个问题,面板的阴影由于旁边的面板而被隐藏。所以我需要一些关于如何解决这个问题的建议。

我试图在它们之间拉开距离,但后来我看到后面有一层白色。我曾尝试更改图层的 z 顺序,但似乎不起作用,所以在不知道该做什么 2 小时后,我在这里问。也许有人知道。

我的代码:

    DropShadow dropShadow2;
    AnchorPane iconPane, menuPane, viewPane;

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

    @Override
    public void start(Stage primaryStage){
        dropShadow2 = new DropShadow();
        dropShadow2.setOffsetX(6.0);
        dropShadow2.setOffsetY(4.0);

        //Main layout
        AnchorPane main_layout = new AnchorPane();

        //Icon layout (left)
        setUpIconLayout();

        //Menu layout (center)
        setUpMenuLayout();

        //View layout (right)
        setUpViewLayout();

        main_layout.getChildren().addAll(iconPane, menuPane, viewPane);

        Scene scene = new Scene(main_layout, 1000, 600);
        primaryStage.setTitle("Delivery System Database");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void setUpIconLayout() {

        iconPane = new AnchorPane();
        iconPane.setPrefSize(50,600);
        String hexColor_left = "D64550";
        iconPane.setStyle("-fx-background-color: #" + hexColor_left);
        iconPane.setEffect(dropShadow2);
    }

    private void setUpMenuLayout() {

        menuPane = new AnchorPane();
        menuPane.setPrefSize(200,600);
        String hexColor_mid = "EA9E8D";
        menuPane.setStyle("-fx-background-color: #" + hexColor_mid);
        menuPane.setEffect(dropShadow2);
        menuPane.setTranslateX(50);
    }

    private void setUpViewLayout() {
        viewPane = new AnchorPane();
        viewPane.setPrefSize(700,600);
        String hexColor_right = "DAEFB3";
        viewPane.setStyle("-fx-background-color: #" + hexColor_right);
        viewPane.setEffect(dropShadow2);
        viewPane.setTranslateX(250);
    }
}

子节点按照它们添加的顺序呈现,因此以相反的顺序添加它们应该就足够了:

main_layout.getChildren().addAll(viewPane, menuPane, iconPane);

正如@VGR 所说,子节点按照它们在子节点中出现的顺序呈现 ObservableList。这意味着较高索引的子项将呈现在较低索引的子项之上。因此,阴影被示例中的其他节点覆盖。由于您使用的是 AnchorPane,它使用开发人员设置的绝对位置,因此一个简单的解决方法是反转子列表的顺序(如 )。

如果您使用的布局还根据子列表的顺序 定位 其子项,例如 HBox,则该解决方案将无法正常工作。您可以通过设置适当的翻译转换来解决此问题,但不推荐这样做。相反,您可以设置子项的 viewOrder 属性(在 JavaFX 9 中添加)。这个属性:

Defines the rendering and picking order of this Node within its parent.

This property is used to alter the rendering and picking order of a node within its parent without reordering the parent's children list. For example, this can be used as a more efficient way to implement transparency sorting. To do this, an application can assign the viewOrder value of each node to the computed distance between that node and the viewer.

The parent will traverse its children in decreasing viewOrder order. This means that a child with a lower viewOrder will be in front of a child with a higher viewOrder. If two children have the same viewOrder, the parent will traverse them in the order they appear in the parent's children list.

由于您希望先前的子项呈现在后续子项之上,您可以简单地将 viewOrder 设置为它们在 children 列表中的索引。这是一个例子:

import java.util.stream.DoubleStream;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        HBox root = new HBox(createRegions(50.0, 200.0, 700.0));
        root.setPadding(new Insets(0.0, 75.0, 0.0, 0.0));
        for (int i = 0; i < root.getChildren().size(); i++) {
            root.getChildren().get(i).setViewOrder(i);
        }
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("ViewOrder Example");
        primaryStage.show();
    }

    private Region[] createRegions(double... prefWidths) {
        return DoubleStream.of(prefWidths).mapToObj(prefWidth -> {
            Region region = new Region();
            region.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
            region.setPrefSize(prefWidth, 600.0);
            region.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
            region.setEffect(new DropShadow(BlurType.THREE_PASS_BOX, Color.BLACK, 10.0, 0.0, 6.0, 4.0));
            region.setBackground(new Background(new BackgroundFill(Color.WHITE, null, null)));
            return region;
        }).toArray(Region[]::new);
    }

}

您提到您尝试过的解决方案之一是设置子项的 Z 顺序,但它不起作用。为了使 z 坐标生效,您必须在创建 Scene 时启用深度缓冲。你可以通过修改上面的例子来测试这个:

  • setViewOrder(i) 更改为 setTranslateZ(i)
  • new Scene(root)更改为new Scene(root, -1.0, -1.0, true)

这有利于在 JavaFX 8 中工作。