java jlabel 在 windows 中工作,但在 linux 中不工作,在屏幕上显示旧格式,openjdk?

java jlabel working in windows but not in linux showing old, formats on screen, openjdk?

所以我写了一个 Java 应用程序,它在屏幕顶部提供了一个透明的 Heads up 显示,它在 windows 上完美运行,但在我的 kubuntu 16.04 机器上它没有更改标签文本时清除旧标签,最终会出现大量重叠的混乱情况。

因为一图值一千字,上图是windows下的样子,下图是kubuntu下的样子:

https://s23.postimg.org/yra0vvlvf/rawr.png

代码如下:

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.net.URL;
import javax.swing.*;
import java.io.*;

public class spob extends JFrame implements WindowFocusListener
{
    public spob()
    {
        if (!SystemTray.isSupported()) {
            System.out.println("SystemTray is not supported");
            return;
        }
        final TrayIcon trayIcon = new TrayIcon((new ImageIcon("icon.png", "trayicon")).getImage());
        final SystemTray tray = SystemTray.getSystemTray();
        trayIcon.setImageAutoSize(true);
        trayIcon.setToolTip("spO2 pr monitor");        
        try {
            tray.add(trayIcon);
        } catch (AWTException e) {
            System.out.println("TrayIcon could not be added.");
            return;
        }
        setType(javax.swing.JFrame.Type.UTILITY);
        setUndecorated(true);
        getContentPane().setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
        setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
        setSize(400, 35);
        JLabel label = new JLabel("Loading...");
        label.setFont(new Font("Tahoma", Font.BOLD, 28));
        label.setForeground(Color.GREEN);
        add(label);
        setLocation(800, 0);
        addWindowFocusListener(this);
        setAlwaysOnTop( true );
        this.setFocusable(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setVisible(true);
        URL url = null;
        BufferedReader in = null;
        String[] anArray = new String[10];
        anArray[0] = "<html><font color=green>- spO2:91  pr:65</font></html>";
        anArray[1] = "<html><font color=red>+ spO2:85  pr:77</font></html>";
        anArray[2] = "<html><font color=green>- spO2:90  pr:68</font></html>";
        anArray[3] = "<html><font color=orange>+ spO2:89  pr:76</font></html>";
        anArray[4] = "<html><font color=orange>- spO2:89  pr:72</font></html>";
        anArray[5] = "<html><font color=orange>+ spO2:88  pr:73</font></html>";
        anArray[6] = "<html><font color=red>- spO2:87  pr:78</font></html>";
        anArray[7] = "<html><font color=red>+ spO2:86  pr:73</font></html>";
        anArray[8] = "<html><font color=green>- spO2:92  pr:74</font></html>";
        anArray[9] = "<html><font color=green>+ spO2:90  pr:71</font></html>";
        while (true){
            try {
                Thread.sleep(200);
                //url = new URL("http://192.168.1.153/stat.php");
                //in = new BufferedReader(new InputStreamReader(url.openStream()));
                //label.setText(in.readLine().toString());
                Random randomno = new Random();
                label.setText(anArray[randomno.nextInt(9 - 1) + 1]);
            } catch (Exception ex) {
            } finally {
                //try {
                //  in.close();
                //} catch (IOException e) {
                //}
            }
        }
    }

    public void windowGainedFocus(WindowEvent e){}
    public void windowLostFocus(WindowEvent e)
    {
        if(e.getNewState()!=e.WINDOW_CLOSED){
            setAlwaysOnTop(false);
            setAlwaysOnTop(true);
        }

    }

    public static void main(String[] args) 
    {
    new spob();
    }
}

所以,一些问题

  • 您违反了 Swing 的单线程规则,本质上是从 EDT 上下文之外更新 UI,如果系统在您尝试绘制某些内容时,这可能会导致问题更新它
  • getContentPane().setBackground(new Color(1.0f,1.0f,1.0f,0.0f)); - Swing 不知道如何处理具有基于 alpha 的颜色的不透明组件,它倾向于不更新它下面的任何组件。

透明 windows 是......有趣......他们往往会提出超出我们通常预期的问题。

在我的 Mac 系统上,我能够重现该问题,但不一致。这一点特别明显,因为 Mac OS 一直在文本周围渲染阴影。

我摆脱的第一件事是setType(javax.swing.JFrame.Type.UTILITY);,我还添加了标签父容器的repaint请求,这似乎已经解决了问题的症状,但同样,我是有时不执行代码。

如果您想定期更新 UI,您应该使用 Swing Timer,请参阅 How to use Swing Timers for more details. If you need to do something in the background and then update the UI, you should use a SwingWorker, have a look Worker Threads and SwingWorker 了解更多详情

(哇哦,我不喜欢我的 gif 动画 :()

该示例特意使用半透明背景,旨在显示框架。将 pane.setAlpha(0.5f); 更改为 pane.setAlpha(0.0f); 以使其完全透明(我也测试过)。

如果您遇到问题,请取消注释 Timer 中的 label.getParent().repaint(); 行,看看是否有帮助

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    private JLabel label;
    private String[] anArray = {
        "<html><font color=green>- spO2:91  pr:65</font></html>",
        "<html><font color=red>+ spO2:85  pr:77</font></html>",
        "<html><font color=green>- spO2:90  pr:68</font></html>",
        "<html><font color=orange>+ spO2:89  pr:76</font></html>",
        "<html><font color=orange>- spO2:89  pr:72</font></html>",
        "<html><font color=orange>+ spO2:88  pr:73</font></html>",
        "<html><font color=red>- spO2:87  pr:78</font></html>",
        "<html><font color=red>+ spO2:86  pr:73</font></html>",
        "<html><font color=green>- spO2:92  pr:74</font></html>",
        "<html><font color=green>+ spO2:90  pr:71</font></html>"
    };
    private Random randomno = new Random();

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setUndecorated(true);
                frame.setAlwaysOnTop(true);
                // Transparent window...
                frame.setBackground(new Color(255, 255, 255, 0));
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                BackgroundPane pane = new BackgroundPane();
                // Set this to 0.0f to make it fully transparent
                pane.setAlpha(0.5f);
                pane.setLayout(new BorderLayout());

                pane.setBorder(new EmptyBorder(10, 10, 10, 10));

                frame.setContentPane(pane);

                label = new JLabel("Loading...");
                label.setFont(new Font("Tahoma", Font.BOLD, 28));
                label.setForeground(Color.GREEN);
                frame.add(label);

                frame.pack();
                Dimension size = frame.getSize();
                size.width = 400;
                frame.setSize(size);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Timer timer = new Timer(200, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        label.setText(anArray[randomno.nextInt(9 - 1) + 1]);
//                      label.getParent().repaint();
                    }
                });
                timer.start();
            }
        });
    }

    public class BackgroundPane extends JPanel {

        private float alpha;

        public BackgroundPane() {
            setOpaque(false);
        }

        public void setAlpha(float alpha) {
            this.alpha = alpha;
            repaint();
        }

        public float getAlpha() {
            return alpha;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getBackground());
            g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
            g2d.fillRect(0, 0, getWidth(), getHeight());
            g2d.dispose();
        }

    }

}

nb 我没有使用 openJDK,我使用的是 Java 8,这可能会有所不同

能力测试

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;

public class Test {

    public static void main(String[] args) {
        GraphicsEnvironment ge
                = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();

        boolean isUniformTranslucencySupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT);
        boolean isPerPixelTranslucencySupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT);
        boolean isShapedWindowSupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT);

        System.out.println("isUniformTranslucencySupported = " + isUniformTranslucencySupported);
        System.out.println("isPerPixelTranslucencySupported = " + isPerPixelTranslucencySupported);
        System.out.println("isShapedWindowSupported = " + isShapedWindowSupported);
    }

}