2

基本上就像标题一样。我在 JavaFX 中有一个节点,我希望根据某些 CSS 规则将其显示在所有其他节点的前面。我不希望这改变我似乎做的节点的VBox顺序.toFront()。看到这个问题。

这甚至可能吗?

编辑:澄清。情况如下。我有一个VBox包含一堆紧密包装ImageView的s。当我将鼠标悬停在一个上时,我希望它稍微变大,让它感觉图像正在从屏幕上抬起。但是由于ImageViews 非常紧密,只有顶部边缘会增长(可见)。底部边缘增大,但低于下图,无法看到。

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

在此处输入图像描述

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

4

3 回答 3

7

这听起来像是使用Java 9 中添加的viewOrder属性的完美情况。控制如何在不改变子列表中 s的顺序的情况下相对于其他s绘制s。这是Javadoc:NodeviewOrderNodeNodeParentNode

定义此节点在其父节点中的渲染和拾取顺序。

此属性用于更改其父节点中节点的呈现和选取顺序,而无需重新排序父节点的子列表。例如,这可以用作实现透明度排序的更有效方式。为此,应用程序可以将每个节点的 viewOrder 值分配给该节点与查看器之间的计算距离。

父级将以递减的 viewOrder 顺序遍历其子级。这意味着 viewOrder 较低的孩子将在 viewOrder 较高的孩子前面。如果两个孩子有相同的viewOrder,则父母会按照他们在父母孩子列表中出现的顺序遍历它们。

但是,viewOrder 不会改变此节点在其父节点中的布局和焦点遍历顺序。在进行布局或焦点遍历时,父级总是按顺序遍历其子级列表。

这是使用此属性的示例:

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,但概念是相同的。当鼠标悬停在 aRectangle上时,它会将视图顺序设置为低于其他顺序,然后播放 aScaleTransition以使其更大。当鼠标退出时,它会将视图顺序重置为0然后反转ScaleTransition.

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

这是一个实际示例的 GIF:

应用程序的 GIF 动图

编辑:既然你提出了 CSS,我就去检查是否可以从样式表中设置视图顺序。它似乎可以。查看CSS 参考指南,有一个为Nodenamed定义的 CSS 属性-fx-view-order

于 2018-06-27T21:47:07.150 回答
2

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

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

简而言之:捕获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;
    }
}

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

已知的问题:

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

于 2018-06-27T21:21:57.290 回答
2

从我的角度来看,您有一个间距为 0 的 VBox,因此每个 ImageView 彼此紧密排列,因此在每个图像中都不能很好地看到发光效果。在这种情况下,您可以在每次想要选择 ImageView 时添加一个边距,以“帮助”出现发光效果。

对于 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;
    }
于 2018-06-27T21:27:52.043 回答