JavaFX 对背景的影响

Posted

技术标签:

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

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

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

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

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

【问题讨论】:

【参考方案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);
    
  

相关问题

Frosted Glass Effect in JavaFX? How do I create a JavaFX transparent stage with shadows on only the border?

【讨论】:

大声笑。你又做了一次: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控制的,它实际上只是操作系统桌面的一部分? 是的!我不知道该怎么办! 可以控制窗口的不透明度。我已经做到了。只是在边缘添加阴影很棘手。此外,我放置的那个链接通过对图像应用效果来工作。我需要知道如何在动态背景上做到这一点。

以上是关于JavaFX 对背景的影响的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX 如何设置场景背景图片

具有透明背景的 JavaFX 按钮

如何在 JavaFX 中创建具有透明背景的启动画面

如何在 JavaFX 中为组布局添加背景颜色?

JavaFX 可拖动背景 - 替代使用相机?

Javafx listview设置背景颜色