具有透明背景的JDialog,模糊了下面的内容 [英] JDialog with transparent background which blurs things underneath

查看:102
本文介绍了具有透明背景的JDialog,模糊了下面的内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个透明的JDialog,以模糊其下方的内容(以提高可读性).已经找到了这个链接,但是对话框的内容在那里模糊.我也发现了​​此,但是这里的所有内容(对话框的内容和内容)都是模糊,并且闪烁.

I'm trying to make a transparent JDialog, which blures what´s underneath it (for better readability). Already found this link, but there the contents of the dialog are blurred. Also I found this, but there everything (things underneath and contents of the Dialog) is blured and it's flickering.

下面是用于更好理解的屏幕截图:

Here's a screenshot for better understanding:

最上面的是我已经拥有的,最下面的是我想要实现的.

The top is what I already have and the bottom what I want to achieve.

推荐答案

经过一些测试,要获得想要实现的目标似乎很棘手.我最终使用了JLayeredPane,其中包含一层用于模糊的背景,一层用于实际的内容.

After some testing it appears to be pretty tricky to get something like what you want to achieve. I ended up using a JLayeredPane which contains one layer for the blurred background and one for the actual content.

我对结果并不完全满意,但是现在没有找到更好的解决方案. 问题是Java没有提供一种捕获实际应用程序背后内容的正确方法,这导致需要在截取屏幕快照之前隐藏内容.这也是导致您提到的闪烁的问题. 为了避免这种情况,我只会在调整应用程序大小或移动应用程序时重新获取背景.一旦真正使用过,应该付出更多的努力,例如如果应用最小化.但是由于此示例无法调整大小或最小化,因此现在并不重要. 但是,这种方法的缺点是它完全忽略了背景是否是静态的.例如,如果您在视频的前面使用该应用程序,则当前版本不会基于视频的当前帧更改背景.如果您在我的示例 setVisible(...)中取消对Timer的注释,则可能会出现这种情况,但随后我们又会闪烁起来.

I am not entirely happy with the result but I didn't find a better solution now. The problem is that Java does not provide a proper way to capture things behind the actual application which leads to the need of hiding the content before taking the screenshot. That also is the problem which causes the flickering you mentioned. To avoid this I do only reaint the background if the application gets resized or moved. There should be put some more effort into this once really used, e.g. if the app gets minimized. But since this example cannot be resized ord minimized it's not really important right now. The downside of this approach however is that it's completely ignoring whether or not the Background isn't static. If you use the application in front of a video for example, the current version does not change the background based on the videos current frame. This may be possible if you uncomment the Timer in my examples setVisible(...) but then we get the flickering back.

我建议您考虑使用JavaFx.由于CSS已经提供了模糊功能,因此它也可能适用于Fx.但是由于我还没有尝试过,所以我不能保证它能正常工作.

I'd suggest you to think about using JavaFx. Since CSS already provides a blur-functionality it may work for Fx as well. But since I didn't try that yet I cannot assure you it's working.

长话短说,这是示例.在上面的链接中使用了模糊功能.

Long story short, here's the example. The blurring function is used from your link above.

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

@SuppressWarnings("serial")
public class BlurryFrame extends JFrame implements ActionListener, ComponentListener {

    public static void main(String... sss) {

        JLabel label1 = new JLabel("TestLabel 1");
        label1.setSize(500, 100);
        label1.setForeground(Color.RED);

        JLabel label2 = new JLabel("TestLabel 2");
        label2.setHorizontalAlignment(SwingConstants.CENTER);
        label2.setForeground(Color.BLUE);

        JPanel testPanel = new JPanel();
        testPanel.setOpaque(false);
        testPanel.setLayout(new BorderLayout());
        testPanel.add(label1, BorderLayout.CENTER);
        testPanel.add(label2, BorderLayout.SOUTH);

        BlurryFrame frame = new BlurryFrame(800, 600, testPanel);
        frame.setBackground(new Color(0, 0, 0, 100));
        frame.setVisible(true);
    }

    private final BackgroundPanel backgroundPane;
    private final JLayeredPane container;
    private final JPanel containerPane;
    private final Robot robot;

    // This rectangle is going to be your screenshots bounds to keep the image only as big as necessary
    private final Rectangle captureRect;

    // This is going to be the blurred screenshot
    private BufferedImage background;

    public BlurryFrame() {
        this(1280, 800);
    }

    public BlurryFrame(int width, int height) {
        this(width, height, null);
    }

    public BlurryFrame(int width, int height, JComponent component) {

        this.captureRect = new Rectangle();
        try {
            this.robot = new Robot();
        } catch (AWTException e) {
            throw new RuntimeException(e);
        }

        this.backgroundPane = new BackgroundPanel();
        this.backgroundPane.setOpaque(false);
        this.backgroundPane.setLayout(new BorderLayout());
        this.backgroundPane.setBounds(0, 0, width, height);
        this.backgroundPane.addComponentListener(this);

        this.containerPane = new JPanel();
        this.containerPane.setOpaque(false);
        this.containerPane.setLayout(new BorderLayout());
        this.containerPane.add(component, BorderLayout.CENTER);
        this.containerPane.setBounds(0, 0, width, height);

        this.setUndecorated(true);
        this.setSize(width, height);
        this.setLocationRelativeTo(null);

        this.container = new JLayeredPane();
        this.container.setOpaque(false);
        this.container.setLayout(new BorderLayout());
        this.container.add(this.backgroundPane, 1);
        this.container.add(this.containerPane, 0);

        this.getContentPane().add(this.container, BorderLayout.CENTER);
    }

    public void add(JComponent component) {
        this.containerPane.add(component, BorderLayout.CENTER);
        this.containerPane.repaint();
    }

    // This method does not really do much but ultimately triggers the screenshot by ending up in #componentHidden(...)
    private void capture() {
        this.containerPane.setVisible(false);
        this.backgroundPane.setVisible(false);
        this.repaint();
    }

    @Override
    public void setVisible(boolean visible) {

        super.setVisible(visible);
        this.capture();

        // XXX uncomment this if you want to see the flickering
        // Timer timer = new Timer(60, this);
        // timer.start();
    }

    @Override
    public void setSize(int width, int height) {

        super.setSize(width, height);

        this.captureRect.setSize(width, height);
        this.backgroundPane.setBounds(0, 0, width, height);
        this.containerPane.setBounds(0, 0, width, height);

        if (this.isVisible())
            this.capture();
    }

    @Override
    public void setSize(Dimension dimension) {

        super.setSize(dimension);

        this.captureRect.setSize(dimension);
        this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
        this.containerPane.setBounds(0, 0, dimension.width, dimension.height);

        if (this.isVisible())
            this.capture();
    }

    @Override
    public void setPreferredSize(Dimension dimension) {

        super.setPreferredSize(dimension);

        this.captureRect.setSize(dimension);
        this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
        this.containerPane.setBounds(0, 0, dimension.width, dimension.height);

        if (this.isVisible())
            this.capture();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        this.capture();
    }

    int i = 0;

    // This is where the magic happens. The capturing needs to be done here in order to assure the applications content has really been hidden
    @Override
    public void componentHidden(ComponentEvent e) {

        int x = this.getLocationOnScreen().x;
        int y = this.getLocationOnScreen().y;

        this.captureRect.setLocation(x, y);

        this.background = this.robot.createScreenCapture(this.captureRect);

        //XXX uncomment this if you want to see what gets captured
        //      if (this.i < 1) {
        //          try {
        //              ImageIO.write(this.background, "png", new File(System.getProperty("user.home") + "\\test.png"));
        //          } catch (IOException ex) {
        //              ex.printStackTrace();
        //          }
        //          this.i++;
        //      }

        this.containerPane.setVisible(true);
        this.backgroundPane.setVisible(true);
        this.repaint();
    }

    @Override
    public void componentMoved(ComponentEvent e) {
        this.capture();
    }

    @Override
    public void componentResized(ComponentEvent e) {
        this.capture();
    }

    private class BackgroundPanel extends JPanel {

        private final float[] matrix = {
                0.111f, 0.111f, 0.111f,
                0.111f, 0.111f, 0.111f,
                0.111f, 0.111f, 0.111f,
        };

        @Override
        public void paintComponent(Graphics g) {

            super.paintComponent(g);

            if (BlurryFrame.this.background != null) {

                BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, this.matrix));
                BufferedImage blurryBack = op.filter(BlurryFrame.this.background, null);

                g.drawImage(blurryBack, 0, 0, null);
            }
        }
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }
}

这篇关于具有透明背景的JDialog,模糊了下面的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆