【问题标题】:JavaFX effect on backgroundJavaFX 对背景的影响
【发布时间】:2014-05-05 00:19:41
【问题描述】:

我正在使用this 制作带有毛玻璃效果的iOS 主题JavaFX2 (Java7) 应用程序。问题是这段代码在 ImageView 上使用了它的效果。我希望它对窗户后面的任何东西都使用它的效果,如下所示:

有没有办法做到这一点?我还想要你在上图中看到的那种小的阴影效果。

明确地说,我不想要那个滑块或任何东西,只是能够透过窗户看到并在边缘有轻微阴影的效果。不过,我想使用 this iOS7-ish effect 而不是 aero。

这可能很重要:我使用的是 Undecorator 的修改版本。

【问题讨论】:

    标签: java user-interface javafx blur dropshadow


    【解决方案1】:

    import javafx.animation.*;
    import javafx.application.*;
    import javafx.beans.property.*;
    import javafx.embed.swing.SwingFXUtils;
    import javafx.geometry.Insets;
    import javafx.scene.*;
    import javafx.scene.control.Label;
    import javafx.scene.effect.*;
    import javafx.scene.Cursor;
    import javafx.scene.Node;
    import javafx.scene.image.*;
    import javafx.scene.layout.StackPane;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    import javafx.stage.StageStyle;
    import javafx.util.Duration;
    
    public class FrostyTech extends Application {
    
        private static final double BLUR_AMOUNT = 10;
    
        private static final Effect frostEffect =
            new BoxBlur(BLUR_AMOUNT, BLUR_AMOUNT, 3);
    
        private static final ImageView background = new ImageView();
        private static final StackPane layout = new StackPane();
    
        @Override public void start(Stage stage) {
            layout.getChildren().setAll(background, createContent());
            layout.setStyle("-fx-background-color: null");
    
            Scene scene = new Scene(
                    layout,
                    200, 300,
                    Color.TRANSPARENT
            );
    
            Platform.setImplicitExit(false);
    
            scene.setOnMouseClicked(event -> {
                    if (event.getClickCount() == 2) Platform.exit();
            });
            makeSmoke(stage);
    
            stage.initStyle(StageStyle.TRANSPARENT);
            stage.setScene(scene);
            stage.show();
    
            background.setImage(copyBackground(stage));
            background.setEffect(frostEffect);
    
            makeDraggable(stage, layout);
        }
    
        // copy a background node to be frozen over.
        private Image copyBackground(Stage stage) {
            final int X = (int) stage.getX();
            final int Y = (int) stage.getY();
            final int W = (int) stage.getWidth();
            final int H = (int) stage.getHeight();
    
            try {
                java.awt.Robot robot = new java.awt.Robot();
                java.awt.image.BufferedImage image = robot.createScreenCapture(new java.awt.Rectangle(X, Y, W, H));
    
                return SwingFXUtils.toFXImage(image, null);
            } catch (java.awt.AWTException e) {
                System.out.println("The robot of doom strikes!");
                e.printStackTrace();
    
                return null;
            }
        }
    
        // create some content to be displayed on top of the frozen glass panel.
        private Label createContent() {
            Label label = new Label("Create a new question for drop shadow effects.\n\nDrag to move\n\nDouble click to close");
            label.setPadding(new Insets(10));
    
            label.setStyle("-fx-font-size: 15px; -fx-text-fill: green;");
            label.setMaxWidth(250);
            label.setWrapText(true);
    
            return label;
        }
    
        // makes a stage draggable using a given node.
        public void makeDraggable(final Stage stage, final Node byNode) {
            final Delta dragDelta = new Delta();
            byNode.setOnMousePressed(mouseEvent -> {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = stage.getX() - mouseEvent.getScreenX();
                dragDelta.y = stage.getY() - mouseEvent.getScreenY();
                byNode.setCursor(Cursor.MOVE);
            });
            final BooleanProperty inDrag = new SimpleBooleanProperty(false);
    
            byNode.setOnMouseReleased(mouseEvent -> {
                byNode.setCursor(Cursor.HAND);
    
                if (inDrag.get()) {
                    stage.hide();
    
                    Timeline pause = new Timeline(new KeyFrame(Duration.millis(50), event -> {
                        background.setImage(copyBackground(stage));
                        layout.getChildren().set(
                                0,
                                background
                        );
                        stage.show();
                    }));
                    pause.play();
                }
    
                inDrag.set(false);
            });
            byNode.setOnMouseDragged(mouseEvent -> {
                stage.setX(mouseEvent.getScreenX() + dragDelta.x);
                stage.setY(mouseEvent.getScreenY() + dragDelta.y);
    
                layout.getChildren().set(
                        0,
                        makeSmoke(stage)
                );
    
                inDrag.set(true);
            });
            byNode.setOnMouseEntered(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    byNode.setCursor(Cursor.HAND);
                }
            });
            byNode.setOnMouseExited(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    byNode.setCursor(Cursor.DEFAULT);
                }
            });
        }
    
        private javafx.scene.shape.Rectangle makeSmoke(Stage stage) {
            return new javafx.scene.shape.Rectangle(
                    stage.getWidth(),
                    stage.getHeight(),
                    Color.WHITESMOKE.deriveColor(
                            0, 1, 1, 0.08
                    )
            );
        }
    
        /** records relative x and y co-ordinates. */
        private static class Delta {
            double x, y;
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }  
    

    相关问题

    【讨论】:

    • 大声笑。你又做了一次:D。还好我在赏金结束前几秒钟就奖励了你。我吓坏了。
    • 嘿!我这样做是为了让您可以在 FrostyPane 顶部加载任何节点,并在窗口不在焦点时关闭模糊(以使其始终保持一致)。我有几个问题: 1. 我删除了将所有内容暂停 50 毫秒的代码,它仍然可以正常工作。有什么我想念的吗? 2. 拖动窗口会重新启动我正在进行的所有功能。例如,当我移动窗口时,我的启动画面会重新启动。有什么办法吗?另外,有什么方法可以轻松实现调整大小?我想我可以应付,但我只是想知道你是否知道任何捷径。
    • 很遗憾,如果背景发生变化,玻璃效果将不会重绘。否则,看起来很棒。
    • @jewelsea 第 69 行“末日机器人来袭!” xaxaxaxa +100
    【解决方案2】:

    你想要的OS依赖窗口装饰的视觉效果,只能通过OS提供的API来实现。因而被下面的StageStyle.TRANSPARENT淘汰了。

    对于 JavaFX 内容本身,您可以控制 stage > scene > root pane 层次结构的视觉效果。舞台和场景不(也不旨在)支持高级样式,因此通过在下面设置为透明来消除。

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        root.setStyle("-fx-background-color: null;");
        root.setPadding(new Insets(10));
    
        DoubleProperty doubleProperty = new SimpleDoubleProperty(0);
    
        Region region = new Region();
        region.styleProperty().bind(Bindings
                .concat("-fx-background-radius:20; -fx-background-color: rgba(56, 176, 209, ")
                .concat(doubleProperty)
                .concat(");"));
        region.setEffect(new DropShadow(10, Color.GREY));
    
        Slider slider = new Slider(0, 1, .3);
        doubleProperty.bind(slider.valueProperty());
    
        root.getChildren().addAll(region, slider);
    
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        Scene scene = new Scene(root, 300, 250);
        scene.setFill(Color.TRANSPARENT);
    
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    

    但是,阴影效果不能很好地与背景颜色的 alpha 值配合使用。您可以通过将阴影的颜色更改为另一种对比色来观察它。

    输出:

    【讨论】:

    • 这个答案也是正确的。我该如何对他们俩说“是”...?
    【解决方案3】:

    要扩展 Jewlsea 的答案 .. 并将上面的示例与 JavaFX 一起使用 ..

    虽然这些类不是公共 API,但它确实完全避免了 AWT 堆栈。 这是一个非公开示例:

    // copy a background node to be frozen over.
        private Image copyBackground(Stage stage) {
            final int X = (int) stage.getX();
            final int Y = (int) stage.getY();
            final int W = (int) stage.getWidth();
            final int H = (int) stage.getHeight();
            final Screen screen = Screen.getPrimary();
            try {
    
                Robot rbt = com.sun.glass.ui.Application.GetApplication().createRobot();
                Pixels p = rbt.getScreenCapture(
                    (int)screen.getBounds().getMinX(),
                    (int)screen.getBounds().getMinY(), 
                    (int)screen.getBounds().getWidth(), 
                    (int)screen.getBounds().getHeight(), 
                    true
                );
    
                WritableImage dskTop = new WritableImage((int)screen.getBounds().getWidth(), (int)screen.getBounds().getHeight());
                dskTop.getPixelWriter().setPixels(
                    (int)screen.getBounds().getMinX(),
                    (int)screen.getBounds().getMinY(),
                    (int)screen.getBounds().getWidth(),
                    (int)screen.getBounds().getHeight(),
                    PixelFormat.getByteBgraPreInstance(),
                    p.asByteBuffer(), 
                    (int)(screen.getBounds().getWidth() * 4)
                );
    
                WritableImage image = new WritableImage(W,H);
                image.getPixelWriter().setPixels(0, 0, W, H, dskTop.getPixelReader(), X, Y);
    
                return image;
            } catch (Exception e) {
                System.out.println("The robot of doom strikes!");
                e.printStackTrace();
    
                return null;
            }
        }
    

    添加了小阴影的结果:

        DropShadow shdw = new DropShadow();
        shdw.setBlurType(BlurType.GAUSSIAN);
        shdw.setColor(Color.GAINSBORO);
        shdw.setRadius(10);
        shdw.setSpread(0.12);
        shdw.setHeight(10);
        shdw.setWidth(10);
        layout.setEffect(shdw);
    

    【讨论】:

      【解决方案4】:

      不透明度是 Node 的一个属性,它是 JavaFX 中用于显示在屏幕上的东西的父类。 http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#opacityProperty

      所以你可以设置你想要淡出的对象的不透明度。然后,您必须添加某种方式来更改所需对象的不透明度。使用图片中的滑块是一种方法,但还有其他方法。

      可以使用 DropShadow 效果来完成投影...http://docs.oracle.com/javafx/2/api/javafx/scene/effect/DropShadow.html。我从来没有使用过它。这个水平有点高,但是如果cmet中有后续问题我可以帮忙回答。

      【讨论】:

      • 那张图片与我需要的代码无关。它只是显示了我所说的“在窗户后面”的意思。我的意思是我希望 this answer 的效果应用于 Java Window 后面的任何内容。
      • 另外,DropShadow 将被应用到窗口后面。如果窗口不透明,您将能够看到它后面的阴影。
      • 啊,我明白了。那么Java窗口背后的对象是不受JavaFX控制的,它实际上只是操作系统桌面的一部分?
      • 是的!我不知道该怎么办!
      • 可以控制窗口的不透明度。我已经做到了。只是在边缘添加阴影很棘手。此外,我放置的那个链接通过对图像应用效果来工作。我需要知道如何在动态背景上做到这一点。
      猜你喜欢
      • 2011-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      相关资源
      最近更新 更多