【问题标题】:Translucent JPanel not clearing background / showing background artifacts in Linux半透明 JPanel 不清除背景/在 Linux 中显示背景工件
【发布时间】:2016-01-29 21:33:16
【问题描述】:

我目前正在开发一个应用程序,该应用程序需要选择屏幕区域的功能。我想出了一个透明的、未装饰的全屏 JFrame,并在其中添加一个半透明、不透明的 JPanel,其中绘制了一个半透明的深色背景以及选择。

虽然这个想法(和代码)在 Windows 上运行良好,但在 linux 上却不同,在调用 repaint() 时 JPanel 的背景似乎没有被清除(即使我告诉它通过各种方法) - 在每个重绘方法中,背景和组件变得越来越暗,等等。

这是 MVCE:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ExampleFrame extends JFrame{


    private ExamplePanel selectionPane;

    public ExampleFrame(){
        this.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {

            }

            @Override
            public void keyPressed(KeyEvent e) {

            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    ExampleFrame.this.dispatchEvent(new WindowEvent(ExampleFrame.this, WindowEvent.WINDOW_CLOSING));
                }
            }
        });


        this.setExtendedState(JFrame.MAXIMIZED_BOTH);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        this.setSize(screenSize);

        this.setUndecorated(true);

        this.setBackground(new Color(255, 255, 255, 0));

        populate();

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setType(Window.Type.UTILITY);
        this.setVisible(true);
    }

    private void populate(){
        this.selectionPane = new ExamplePanel();
        this.setContentPane(selectionPane);
    }

    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ExampleFrame();
            }
        });
    }

    public static class ExamplePanel extends JPanel{

        private static Color bg = new Color(0,0,0,0.5f);

        private int sx = -1, sy = -1, ex = -1, ey = -1;

        public ExamplePanel(){

            MouseAdapter mouseAdapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    sx = sy = ex = ey = -1;

                    sx = e.getX();
                    sy = e.getY();
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    ex = e.getX();
                    ey = e.getY();
                    repaint();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    ex = e.getX();
                    ey = e.getY();
                    repaint();
                }
            };

            this.addMouseListener(mouseAdapter);
            this.addMouseMotionListener(mouseAdapter);

            this.setDoubleBuffered(false);
            this.setOpaque(false);
            this.setBackground(bg);
        }

        @Override
        public void paintComponent(Graphics g){
            Graphics2D g2 = (Graphics2D)g.create();

            g2.setComposite(AlphaComposite.Clear);
            g2.setBackground(new Color(255, 255, 255, 0));
            g2.fillRect(0, 0, getWidth(), getHeight());
            //g2.clearRect(0, 0, getWidth(), getHeight()); //neither of them work

            g2.setComposite(AlphaComposite.Src.derive(.5f));
            g2.setPaint(getBackground());
            g2.fillRect(0, 0, getWidth(), getHeight());

            g2.setComposite(AlphaComposite.Src.derive(1f));
            g2.setPaint(Color.WHITE);
            g2.drawString("Press Escape to exit", 10, 20);

            if(!(sx == -1 || sy == -1 || ex == -1 || ey == -1)){

                int asx = Math.min(sx, ex);
                int asy = Math.min(sy, ey);

                int w = Math.abs(ex - sx);
                int h = Math.abs(ey - sy);

                g2.setComposite(AlphaComposite.Src);
                g2.setPaint(new Color(255, 255, 255, 0));
                g2.fillRect(asx, asy, w, h);

                g2.setPaint(new Color(0, 0, 0, 1));
                g2.fillRect(asx, asy, w, h);

                g2.setComposite(AlphaComposite.SrcOver);
                g2.setStroke(new BasicStroke(2));
                g2.setPaint(new Color(1, 1, 1, 0.15f));
                g2.drawRect(asx-1,asy-1, w+2, h+2);
            }
        }
    }

}

关于可能导致这种情况的任何想法?或者这可能是 Linux 上 Java 的一个错误?我在 Windows 10 和 Ubuntu 14.04 LTS 以及运行 KDE gui 的未知版本 Arch Linux 下测试过这个(由朋友测试)

编辑:也在 OSX (Yosemite & El capitan) 下测试,都运行良好。

【问题讨论】:

  • 有什么理由将双缓冲设置为 false?
  • 除了网上有人说它可能有帮助 - 没有。
  • 好的,双缓冲将防止更新闪烁,在我的测试中,启用后似乎可以更快地渲染。所以我一直在 MacOS 上运行你的代码,但似乎无法复制这些问题,但是,你真的应该调用 super.paint 并且“清除”功能是多余的。对于不同的方法,您可以使用类似this
  • this
  • 该应用在 MacOS 上运行良好,从来没有出现过问题。此外,该应用程序必须在框架后面显示实时屏幕,因此不能选择在每个框架中使用机器人截取屏幕。

标签: java linux swing


【解决方案1】:
this.setBackground(new Color(255, 255, 255, 0));

如果您想要一个完全透明的组件,那么只需使用:

component.setOpaque( false );

这告诉 Swing 寻找父组件并首先绘制它,这样您就不会得到绘制工件。

private static Color bg = new Color(0,0,0,0.5f);

如果您想要半透明背景,则需要进行自定义编码,因为 Swing 不支持此功能。

查看Backgrounds With Transparency 了解有关此主题的更多信息和一些解决方案。

一种是使用如下代码进行自己的自定义绘画:

JPanel panel = new JPanel()
{
    protected void paintComponent(Graphics g)
    {
        g.setColor( getBackground() );
        g.fillRect(0, 0, getWidth(), getHeight());
        super.paintComponent(g);
    }
};

另一个解决方案是一个可重用的类,它可以与任何组件一起使用,因此您不需要自定义每个组件。 panel.setOpaque(false); // 先绘制父级背景 panel.setBackground(新颜色(255, 0, 0, 20)); frame.add(面板);

【讨论】:

  • 你告诉我摇摆不能那样做,但是你把我重定向到一个显示相同代码的站点,只使用整数而不是浮点数。此外,JFrames 没有 setOpaque 方法。
  • @TheMorfeus, upon each repaint method, the background and the component get darker and darker, etc. - 这与链接中描述的行为完全相同,这就是我将您引导至该链接的原因。我想不同之处在于您还使用了透明框架。很抱歉,它没有帮助。
  • @TheMorfeus 调用 setBackground 并将其传递给基于 alpha 的颜色仅适用于 JFrame,它不适用于其他组件,您需要通过首先使组件透明然后绘画来伪造它自己的背景颜色
  • @MadProgrammer 我正在这样做,我根本没有使用标准的paintComponent。
  • @TheMorfeus,您是否尝试过简化您的绘画代码以查看是否可以只绘制透明背景?我给你的链接中的代码在绘制背景时不使用合成。 MCVE 应该是最简单的代码。您拥有各种与复合相关的代码。也许这就是问题所在。我无法测试,因为我使用的是 Windows,你说这不是问题。
【解决方案2】:

如果无法复制问题,就很难知道问题的确切原因。代码中有许多需要关注的领域...

  • 不调用super.paintComponent 不尊重绘制调用链
  • setDoubleBuffered(false)的使用
  • JPanel 上使用 this.setBackground(bg); 并将基于 alpha 的颜色传递给它
  • AlphaComposite 的广泛使用以及尝试清除 Graphics 上下文的谨慎使用

基本的操作过程是简化绘制过程,直到您可以确定导致问题的操作或操作组合。

或者采取另一种方法。与其使用不同AlphaComposite 设置的组合,不如考虑只使用Area 并从中减去您想要暴露的区域......

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ExampleFrame extends JFrame {

    private ExamplePanel selectionPane;

    public ExampleFrame() {
        this.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {

            }

            @Override
            public void keyPressed(KeyEvent e) {

            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    ExampleFrame.this.dispatchEvent(new WindowEvent(ExampleFrame.this, WindowEvent.WINDOW_CLOSING));
                }
            }
        });

        this.setExtendedState(JFrame.MAXIMIZED_BOTH);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        this.setSize(screenSize);

        this.setUndecorated(true);

        this.setBackground(new Color(255, 255, 255, 0));

        populate();

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setType(Window.Type.UTILITY);
        this.setVisible(true);
    }

    private void populate() {
        this.selectionPane = new ExamplePanel();
        this.setContentPane(selectionPane);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ExampleFrame();
            }
        });
    }

    public static class ExamplePanel extends JPanel {

        private static Color bg = new Color(0, 0, 0);

        private int sx = -1, sy = -1, ex = -1, ey = -1;

        public ExamplePanel() {

            MouseAdapter mouseAdapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    sx = sy = ex = ey = -1;

                    sx = e.getX();
                    sy = e.getY();
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    ex = e.getX();
                    ey = e.getY();
                    repaint();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    ex = e.getX();
                    ey = e.getY();
                    repaint();
                }
            };

            this.addMouseListener(mouseAdapter);
            this.addMouseMotionListener(mouseAdapter);

            this.setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g.create();

            Area area = new Area(new Rectangle(0, 0, getWidth(), getHeight()));

            if (!(sx == -1 || sy == -1 || ex == -1 || ey == -1)) {

                int asx = Math.min(sx, ex);
                int asy = Math.min(sy, ey);

                int w = Math.abs(ex - sx);
                int h = Math.abs(ey - sy);

                area.subtract(new Area(new Rectangle(asx - 1, asy - 1, w + 2, h + 2)));
            }
            g2.setComposite(AlphaComposite.Src.derive(.25f));
            g2.setPaint(bg);
            g2.fill(area);

            g2.setComposite(AlphaComposite.Src.derive(1f));
            g2.setPaint(Color.WHITE);
            g2.drawString("Press Escape to exit", 10, 20);
            g2.dispose();
        }
    }

}

【讨论】:

  • 即使我的代码存在所有问题,加上区域,它仍然无法在 Linux 上运行并且无法重新绘制。我的代码的问题是许多实验的结果,无论它们是否存在,它都不起作用
  • 好的,所以去掉你的代码。从一个不透明的窗口开始,看看你是否可以做到这一点。如果可行,则包括透明窗口,如果失败,至少您已经确定了问题或打折了其中的一部分。这可能只是 Linux 的问题,不能说,没有安装 linux 来测试。我建议以这种方式使用Area 问题会稍微小一些,因为您不太可能混淆AlphaComposite 设置
  • 只是这样做了,一切正常,直到我在 JFrame 上添加了透明度。所以我想Linux上的Java确实存在问题。即使来自here(每像素半透明)的代码也不能正常工作,也不能清除透明窗口的背景。韦尔普。此外,使用Area 是不可接受的,因为AlphaComposite/Graphics.clearRect 也会清除在面板上绘制的文本,而Area 不会。
  • 好吧,因为我不知道你真正想要达到什么目标,所以很难提出建议;)
【解决方案3】:

如果您想清除背景,请务必致电super.paintComponent()

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g.create();

【讨论】:

  • 哦,对了。我从我的代码中删除了它,因为有人告诉其他人它可能因此而无法工作。是的,我已经冒险寻找答案。 TL;DR:不会改变任何东西。
猜你喜欢
  • 2015-11-12
  • 2014-07-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2023-03-22
  • 2012-07-27
  • 2013-03-30
  • 2017-10-07
相关资源
最近更新 更多