You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下Java Swing窗口在副显示器上丢失透明度问题

临时解决跨显示器Swing窗口透明失效问题

我有一段Swing代码,主显示器上能正常渲染透明窗口,但把窗口手动或通过程序移到副显示器后,透明度直接丢失,背景变成黑色。这个问题在Redhat 8.2/8.4系统的OpenJDK 1.8/11/17版本里都能复现。已经给OpenJDK提交了Bug等官方修复,现在急需临时解决方案或者猴子补丁。

正常效果:
透明度正常

异常效果:
透明度异常

测试代码:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparencyTest {
    public static void main(String[] args) {
        JPanel panel = new Custom();
        panel.setOpaque(false);
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setBackground(new Color(0, 0, 0, 0));
        frame.setTitle("Test");
        frame.getContentPane().add(panel);
        frame.setSize(320, 240);
        frame.setResizable(false);
        frame.setMinimumSize(new Dimension(1, 1));
        frame.setLocation(1920, 0);
        frame.setVisible(true);
    }
    private static class Custom extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.clearRect(0, 0, getWidth(), getHeight());
            g.setColor(new Color(255, 0, 0, 127));
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }
}

临时解决方案

1. 监听窗口移动,重置透明背景

窗口挪到新显示器时,强制重新设置透明背景并触发重绘,能快速修复黑色背景问题:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparencyTest {
    public static void main(String[] args) {
        JPanel panel = new Custom();
        panel.setOpaque(false);
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setBackground(new Color(0, 0, 0, 0));
        frame.setTitle("Test");
        frame.getContentPane().add(panel);
        frame.setSize(320, 240);
        frame.setResizable(false);
        frame.setMinimumSize(new Dimension(1, 1));
        
        // 加个位置变化监听器
        frame.addComponentListener(new ComponentAdapter() {
            private Point lastPos;
            
            @Override
            public void componentMoved(ComponentEvent e) {
                Point currentPos = frame.getLocation();
                // 检测位置变化(如果是跨显示器移动,坐标会有明显跳转)
                if (lastPos != null && !currentPos.equals(lastPos)) {
                    // 重置透明背景+强制重绘
                    frame.setBackground(new Color(0, 0, 0, 0));
                    frame.repaint();
                }
                lastPos = currentPos;
            }
        });
        
        frame.setLocation(1920, 0);
        frame.setVisible(true);
    }
    private static class Custom extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.clearRect(0, 0, getWidth(), getHeight());
            g.setColor(new Color(255, 0, 0, 127));
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }
}

2. 直接操作X11窗口属性(针对Redhat的Xorg环境)

绕开AWT的渲染问题,直接给X11窗口设置透明度属性,这个方法依赖OpenJDK内部API,仅作临时补丁用:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Window;
import java.lang.reflect.Method;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparencyTest {
    public static void main(String[] args) {
        JPanel panel = new Custom();
        panel.setOpaque(false);
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setTitle("Test");
        frame.getContentPane().add(panel);
        frame.setSize(320, 240);
        frame.setResizable(false);
        frame.setMinimumSize(new Dimension(1, 1));
        frame.setLocation(1920, 0);
        frame.setVisible(true);
        
        // 设置X11窗口全局透明度
        setX11WindowOpacity(frame, 1.0f);
        // 保留AWT背景透明设置
        frame.setBackground(new Color(0, 0, 0, 0));
    }
    
    private static void setX11WindowOpacity(Window window, float opacity) {
        try {
            // 反射获取X11窗口实例
            Method getPeer = Window.class.getDeclaredMethod("getPeer");
            getPeer.setAccessible(true);
            Object peer = getPeer.invoke(window);
            
            Method getWindow = peer.getClass().getMethod("getWindow");
            long x11Window = (Long) getWindow.invoke(peer);
            
            // 加载X11库并设置透明度属性
            System.loadLibrary("X11");
            Class<?> xlib = Class.forName("sun.awt.X11.XlibWrapper");
            Method xChangeProp = xlib.getDeclaredMethod(
                "XChangeProperty",
                long.class, long.class, long.class, int.class, int.class, byte[].class
            );
            
            // 获取_NET_WM_WINDOW_OPACITY的原子值
            long opacityAtom = getAtom("_NET_WM_WINDOW_OPACITY");
            // 把透明度转成X11需要的32位大端字节数组
            int opacityInt = Float.floatToIntBits(opacity);
            byte[] opacityBytes = new byte[]{
                (byte)(opacityInt >> 24),
                (byte)(opacityInt >> 16),
                (byte)(opacityInt >> 8),
                (byte)opacityInt
            };
            
            xChangeProp.invoke(null, x11Window, opacityAtom, 6 /*XA_CARDINAL*/, 32, 0 /*PropModeReplace*/, opacityBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static long getAtom(String atomName) throws Exception {
        Class<?> xlib = Class.forName("sun.awt.X11.XlibWrapper");
        Method xInternAtom = xlib.getDeclaredMethod("XInternAtom", long.class, String.class, boolean.class);
        long display = (Long) xlib.getDeclaredMethod("getDefaultDisplay").invoke(null);
        return (Long) xInternAtom.invoke(null, display, atomName, false);
    }
    
    private static class Custom extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.clearRect(0, 0, getWidth(), getHeight());
            g.setColor(new Color(255, 0, 0, 127));
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }
}

3. 启用OpenGL渲染加速

试试强制Swing用OpenGL渲染,可能解决跨显示器的渲染不一致问题:

  • 启动程序时加参数:-Dsun.java2d.opengl=true
  • 或者在代码开头设置:
System.setProperty("sun.java2d.opengl", "true");

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 05:28:11