JavafX & CSS - 将节点移到前面

JavafX & CSS - Move Node to Front

基本如题。我在 JavaFX 中有一个节点,我想根据某些 CSS 规则将其显示在所有其他节点的前面。我不希望这改变我的 VBox 中节点的顺序,而 .toFront() 似乎是这样做的。请参阅 this 问题。

这可能吗?

编辑: 澄清一下。情况如下。我有一个 VBox,其中包含一堆紧密排列的 ImageView。当我将鼠标悬停在一个上时,我希望它稍微变大,给人一种图像从屏幕上移开的感觉。但是由于 ImageView 非常紧凑,只有顶部边缘(可见)增长。底边长了但是在下图下面看不到。

编辑 2: 根据要求,这里是我正在做的事情的截图。

不同的颜色渐变是 ImageViews,当我将鼠标悬停在一个渐变上时,它应该像这张图片中顶部渐变的顶部边缘一样增长(仔细观察 X 旁边的右上角) .然而,正如在此图像中也可见的那样,此 ImageView 的底部边缘已被此 VBox 中的下一个渐变隐藏,并且增长不可见。

这是一个这样的解决方案,创建一个新的舞台来显示放大的图像。

我没有在此示例中设置正确的坐标,但这可以作为概念证明。

简而言之:捕获 onMouseEnteredonMouseExited 事件并隐藏或显示新舞台。

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        FlowPane root = new FlowPane();
        root.setHgap(5);
        root.setVgap(5);
        for (int i = 0; i < 25; i++) {
            root.getChildren().add(getImagePane());
        }

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private VBox getImagePane() {

        VBox pane = new VBox() {{
            setAlignment(Pos.CENTER);
            setPadding(new Insets(5));
            setWidth(50);
            setHeight(50);
            setStyle("-fx-border-color: black");
        }};

        ImageView img = new ImageView("sample/imageGallery/cerulean.png") {{
            setFitWidth(50);
            setFitHeight(50);
            setPreserveRatio(true);

            Stage stage = zoomedStage(pane, this);

            setOnMouseEntered(mouseEvent -> stage.show());
            setOnMouseExited(mouseEvent -> stage.hide());

        }};

        pane.getChildren().add(img);
        return pane;

    }

    private Stage zoomedStage(VBox parent, ImageView img) {

        Stage stage = new Stage();
        stage.setWidth(110);
        stage.setHeight(110);

        VBox pane = new VBox() {{
            setAlignment(Pos.CENTER);
            setPadding(new Insets(5));
            setWidth(110);
            setHeight(110);
            setStyle("-fx-border-color: black");
        }};

        pane.setPickOnBounds(false);

        ImageView zoomedImage = new ImageView(img.getImage());
        zoomedImage.setFitHeight(100);
        zoomedImage.setFitWidth(100);

        pane.getChildren().add(zoomedImage);

        stage.setScene(new Scene(pane));
        stage.initStyle(StageStyle.UNDECORATED);
        return stage;
    }
}

从这里开始,应该只是一个问题,或者将舞台的坐标固定在图像的中心,然后移除舞台的装饰。

已知问题:

您还需要处理鼠标光标被新阶段阻挡的问题。这将导致鼠标不断进入和退出图像缩略图的循环,导致放大的舞台闪烁。

从我的角度来看,您有一个间距为 0 的 VBox,因此每个 ImageView 都紧密地排列在一起,因此发光效果在每个图像中都不是很明显。在这种情况下,您可以在每次想要 select ImageView 时添加边距,以便 'help' 出现发光效果。

对于Java 8 :

遗憾的是,CSS 不可能发生这种情况,因为 ImageView 不提供任何设置边距或填充的规则。因此,您或多或少(在我看来)一定会通过代码编写该行为。 :

private Node createImageView(String imageLink) {
        // Setting the image view
        ImageView imageView = new ImageView(imageLink);

        // setting the fit width of the image view
        imageView.setFitWidth(400);

        // Setting the preserve ratio of the image view
        imageView.setPreserveRatio(true);

        // Instantiating the Glow class
        Glow glow = new Glow();

        imageView.setOnMouseEntered(e -> {
            // setting level of the glow effect
            glow.setLevel(0.9);
            // Adding a margin on TOP and Bottom side just to make the 
            // glowing effect visible
            VBox.setMargin(imageView, new Insets(2,0,2,0));
        });

        imageView.setOnMouseExited(e -> {
            // remove the glow effect
            glow.setLevel(0.0);
            // remove the margins 
            VBox.setMargin(imageView, new Insets(0));
        });

        // Applying bloom effect to text
        imageView.setEffect(glow);

        return imageView;
    }

这听起来像是使用 Java 中添加的 NodeviewOrder 属性 的完美情况 9. viewOrder 控制如何 Node 是相对于相同 Parent 的其他 Node 绘制的,而不更改子列表中 Node 的顺序。这是 Java文档:

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.

However, viewOrder does not alter the layout and focus traversal order of this Node within its parent. A parent always traverses its children list in order when doing layout or focus traversal.

这是一个使用这个 属性 的例子:

import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.ArrayList;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        var box = new HBox(createRectangles(Color.DARKBLUE, Color.FIREBRICK, 25));
        box.setAlignment(Pos.CENTER);
        box.setPadding(new Insets(50, 20, 50, 20));
        primaryStage.setScene(new Scene(box));
        primaryStage.show();
    }

    private Rectangle[] createRectangles(Color start, Color end, int count) {
        var list = new ArrayList<Rectangle>(count);
        for (double i = 0; i < count; i++) {
            var rect = new Rectangle(30, 60, start.interpolate(end, i / count));

            var scaleTrans = new ScaleTransition(Duration.millis(250), rect);
            scaleTrans.setFromX(1.0);
            scaleTrans.setFromY(1.0);
            scaleTrans.setToX(1.2);
            scaleTrans.setToY(1.2);

            rect.setOnMouseEntered(e -> {
                scaleTrans.stop(); // <--- doesn't seem necessary*
                scaleTrans.setRate(1.0);
                rect.setViewOrder(-1.0);
                scaleTrans.play();
            });
            rect.setOnMouseExited(e -> {
                scaleTrans.stop(); // <--- doesn't seem necessary*
                scaleTrans.setRate(-1.0);
                rect.setViewOrder(0.0);
                scaleTrans.play();
            });
            // *the "stop()"'s don't seem to be necessary. When I commented
            // them out the animation still worked. In fact, the animation
            // actually seems smoother in the situation where you move the
            // mouse over and then away quickly (before the zoom-in completes).

            list.add(rect);
        }
        return list.toArray(new Rectangle[0]);
    }

}

它使用 Rectangles 而不是 ImageViews 但概念是相同的。当鼠标悬停在 Rectangle 上时,它会将视图顺序设置为低于其他视图,然后播放 ScaleTransition 使其变大。当鼠标退出时,它会将视图顺序重置回 0,然后反转 ScaleTransition.

注意:我使用了 Java 10 中添加的 var 关键字。

下面是实际示例的 GIF:

编辑: 自从你提出 CSS 我去检查是否可以从样式表设置视图顺序。看起来它可以。查看 CSS Reference Guide 有一个 CSS 属性 为 Node 定义,名为 -fx-view-order