5

在(嵌入式)Ubuntu 12.04 系统上,我们有一个简单的 Java 程序,它在窗口上显示一些图形模式,大约每秒更新一次。我们用它来监控系统上运行的一些进程。问题在于,虽然它处于活动状态且未最小化,但它会在窗口更新时窃取焦点。这使得使用打开的终端窗口变得不可能。

运行应用程序表单命令行或从 Eclipse IDE 时,行为是相同的。

在 NetBeans IDE 下运行时,在 Windows 7 上不会出现同样的问题。

我们如何防止 Java 应用程序窃取 Ubuntu 机器上的焦点?


更新 1:发现这个问题似乎与同样的问题有困难: 如何停止/解决 Java 应用程序在 Linux 窗口管理器中窃取焦点。阅读它,我了解到问题在于使用 JFrame 作为容器,这就是我们使用的。他们的解决方案是使用 JWindow 容器而不是 JFrame 容器。然而,在寻找差异时,JWindow 是“裸露的”,不像“真正的”窗口,因为没有装饰。有没有办法JFrame 中使用 JWindow,从而消除焦点窃取?


更新 2:尝试在 PC 上的 Ubuntu 虚拟机上运行此程序会产生相同的不当行为。这表明 Windows 7 和 Linux 的 Java 运行时存在差异,并且该问题并非特定于 Embedded linux。


更新 3:这是一个 SSCCE:

//package SSCCE;

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

public class MonitorSSCCE extends JFrame{

    public static void main(String[] args) 
    {
        // Set the frame
        JFrame ecoreFrame = new JFrame("SSCCE");
        ecoreFrame.setSize(120, 120);
        ecoreFrame.setVisible(true);

        // Refresh frame every 200 msec
        while (true) {
            GRFX grfx = new GRFX();
            ecoreFrame.add(grfx);
            ecoreFrame.setVisible(true);
            grfx = null;

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {

            }
        }
    }


    static int clr = 0;

    public static class GRFX extends JPanel {

        public void paintComponent(Graphics comp) {
            Graphics2D comp2D = (Graphics2D) comp;

            // Draw a changin color rectangle
            comp2D.setColor(new Color(clr, 0, 0));
            Rectangle2D.Double rect = new Rectangle2D.Double(10, 10, 100, 100);
            comp2D.fill(rect);
            clr = clr + 10;
            if (clr > 255)
                clr = 0;
        }
    }
}

更新 4:在准备 SSCCE 时,我阅读并了解了 JFrame 对象的大量窗口刷新方法。碰巧问题出在循环setVisible()内的调用上。while解决方案是用repaint()方法替换它。

4

2 回答 2

2

在带有Unity窗口管理器的 Ubuntu 12.04.2 上,此示例在桌面上以 1 Hz 的频率正常运行,同时终端和软件更新也在运行。比较的几个参考点:

  • Swing GUI 对象应该事件分派线程上构建和操作。

  • 避免混合使用重量级和轻量级组件

  • 避免覆盖paint();“Swing 程序应该覆盖paintComponent()而不是覆盖paint()。”—<a href="http://www.oracle.com/technetwork/java/painting-140037.html#callbacks" rel="nofollow noreferrer">在 AWT 和 Swing 中绘画: 绘画方法

  • 不要调用updateUI()pack()代替validate().

附录:我在下面重新考虑了您的示例以说明一些附加点:

图片

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @see https://stackoverflow.com/a/18455006/230513
 */
public class TestGRFX {

    private static class GRFX extends JPanel {

        private static final int N = 256;
        private float clr = 0;
        private Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, N, N);
        private Timer t = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                GRFX.this.repaint();
            }
        });

        public void start() {
            t.start();
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2D = (Graphics2D) g;
            g2D.setColor(new Color(Color.HSBtoRGB(clr, 1, 1)));
            g2D.fill(rect);
            clr += .01;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(N, N);
        }
    }

    private void display() {
        JFrame f = new JFrame("TestGRFX");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GRFX g = new GRFX();
        f.add(g);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        g.start();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestGRFX().display();
            }
        });
    }
}
于 2013-08-27T00:09:02.810 回答
0

通过消除问题替换循环setVisible()内的方法调用:while()repaint()

    // Refresh frame every 200 msec
    while (true) {
        GRFX grfx = new GRFX();
        ecoreFrame.add(grfx);
        ecoreFrame.repaint();
        grfx = null;

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {

        }
    }
于 2013-08-27T16:07:27.783 回答