相关文章推荐
冷冷的电影票  ·  Caused by: ...·  3 周前    · 
跑龙套的玉米  ·  Fun with Numbers in chmod·  6 月前    · 
强悍的长颈鹿  ·  EXCEL VBA的SAVEAS问题·  1 年前    · 

半透明的JPNEL不能清除背景/在Linux下显示背景伪影

2 人关注

我目前正在开发一个应用程序,它需要选择屏幕区域的功能。我想到了创建一个透明的、无装饰的、全屏的JFrame,并在其中添加一个半透明的、非不透明的Jpanel,在其中画上半透明的深色背景以及选择。

虽然这个想法(和代码)在Windows上运行良好,但在Linux上就不一样了,Jpanel的背景似乎不会在调用repaint()时被清除(即使我通过各种方法告诉它)--在每个repaint方法中,背景和组件都变得越来越暗,等等。

Here's the 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下测试过这个问题(由一个朋友测试)。

EDIT: also tested under OSX (Yosemite & El capitan), both worked fine.

5 个评论
你把双重缓冲设置为假的原因是什么?
除了互联网上有人告诉它可能有帮助外--没有。
好的,双重缓冲可以防止更新的闪烁,在我的测试中,当启用时似乎可以使它渲染得更快。所以我在MacOS上运行了你的代码,但似乎无法复制这些问题,然而,你真的应该调用 super.paint ,"清除 "功能是多余的。 对于一个不同的方法,你可以使用这样的东西 this
该应用程序在MacOS上运行良好,从未出现过问题。此外,该应用程序必须在框架后面显示实时屏幕,所以用机器人的每一帧进行截图是不可能的。
java
linux
swing
themorfeus
themorfeus
发布于 2016-01-30
3 个回答
camickr
camickr
发布于 2016-01-30
已采纳
0 人赞同
this.setBackground(new Color(255, 255, 255, 0));

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

component.setOpaque( false );

这告诉Swing寻找父级组件并首先绘制它,这样你就不会得到绘制的假象。

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

如果你想要半透明的背景,那么需要做自定义编码,因为Swing不支持这个。

Check out 具有透明度的背景以了解有关这一主题的更多信息和一些解决方案。

一个是用类似的代码做你自己的自定义绘画。

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( new Color(255, 0, 0, 20) ) 。 frame.add(panel);

你告诉我swing不能这样做,但你把我转到一个网站,上面显示了同样的代码,只是使用了ints而不是floats。另外,JFrames没有setOpaque方法。
@TheMorfeus, upon each repaint method, the background and the component get darker and darker, etc. 。- 这与链接中描述的行为完全相同,这也是我把你引向链接的原因。我想不同的是,你也在使用一个透明的框架。很抱歉,这没有帮助。
@TheMorfeus 调用 setBackground 并传递给它基于阿尔法的颜色,只对 JFrame 有效,对其他组件无效,你需要先让组件透明,然后自己画出背景色来伪造它。
@MadProgrammer 我正在这么做,我根本没有使用标准的paintComponent。
@TheMorfeus,你有没有试着简化你的绘画代码,看看你是否可以直接画一个透明的背景?我给你的链接中的代码在绘制背景时并没有使用复合体。一个MCVE应该是最简单的代码。你有各种与复合有关的代码。也许这就是问题所在。我无法测试,因为我使用的是Windows,而你说这不是一个问题。
MadProgrammer
MadProgrammer
发布于 2016-01-30
0 人赞同

在没有能够复制它的情况下,很难知道问题的确切原因。 在代码中,有许多值得关注的地方...

  • Not honouring the paint call chain by not calling super.paintComponent
  • The use of setDoubleBuffered(false)
  • The use of this.setBackground(bg); on a JPanel and passing an alpha based color to it
  • The extensive use of AlphaComposite and it's scrupulous use to try and clear the Graphics context
  • 基本的行动方针是简化喷漆过程,直到你能确定导致问题的行动或行动组合。

    或者采取另一种方法。 与其使用不同的 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();
        
    即使我的代码有这么多问题,再加上Area,它仍然不能在Linux上工作,不能重绘。我的代码中的问题是许多实验的结果,无论它们是否存在,它都不能工作。
    好吧,那就把你的代码剥离出来。 从一个非透明的窗口开始,看看你是否能使其工作。 如果成功了,再加上透明的窗口,如果失败了,至少你已经发现了问题,或者说是排除了问题的一部分。 这可能只是Linux的一个问题,我不能说,我没有安装Linux来测试。我建议用 Area 这种方式问题会少一点,因为你不可能混淆 AlphaComposite 的设置。
    就这样做了,一切都很好,直到我在JFrame上增加了透明度。所以我想Linux上的Java确实有问题。即使是来自 here (Per-pixel translucency)不能正确工作,不能清除透明窗口的背景。好吧。 另外,使用 Area 也是不行的,因为 AlphaComposite / Graphics.clearRect 也会清除画在面板上的文字,而 Area 则不会。
    好吧,因为我不知道你实际上想达到什么目的,所以很难提出建议;)
    martinez314
    martinez314
    发布于 2016-01-30
    0 人赞同

    如果你想清除背景,请务必调用 super.paintComponent()