Как не дать графической программе Java украсть фокус при обновлении окна?
В (встроенной) системе Ubuntu 12.04 у нас есть простая программа на Java, которая отображает некоторые графические шаблоны в окне, обновляя их каждую секунду или около того. Мы используем его для мониторинга некоторых процессов, работающих в системе. Проблема в том, что, хотя он активен и не свернут, он крадет фокус при каждом обновлении окна. Это делает невозможной работу с открытыми оконными окнами.
То же самое происходит при запуске командной строки формы приложения или из Eclipse IDE.
Та же проблема не возникает в Windows 7 при работе в среде IDE NetBeans.
Как мы можем помешать приложению Java сфокусироваться на машине с Ubuntu?
ОБНОВЛЕНИЕ 1: нашел этот вопрос, который, кажется, борется с той же самой проблемой: Как мне остановить / обойти приложения Java, крадущие фокус в менеджерах окон Linux. Прочитав его, я узнал, что проблема заключается в использовании JFrame в качестве контейнера, что мы и используем. Их решением было использование контейнера JWindow вместо контейнера JFrame. Однако, ища разницу, JWindow "голый" и не ведет себя как "реальное" окно, так как там нет декораций. Есть ли способ использовать JWindow внутри JFrame и, таким образом, устранить кражу фокуса?
ОБНОВЛЕНИЕ 2: Попытка запустить эту программу на виртуальной машине Ubuntu на ПК приводит к тому же неправильному поведению. Это говорит о том, что существует разница во времени выполнения Java в Windows 7 и Linux, и что проблема не является специфичной для встроенного 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()
метод.
2 ответа
Замена 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) {
}
}
В Ubuntu 12.04.2 с оконным менеджером Unity этот пример обычно анимируется на рабочем столе в фоновом режиме с частотой 1 Гц, когда также запущены обновление терминала и программного обеспечения. Несколько ориентиров для сравнения:
Графические объекты Swing должны создаваться и обрабатываться только в потоке диспетчеризации событий.
Избегайте смешивания тяжелых и легких компонентов.
Избегайте переопределения
paint()
; "Программы Swing должны переопределитьpaintComponent()
вместо переопределенияpaint()
."- Живопись в AWT и Swing: Методы рисования.Не призывать
updateUI()
или жеpack()
в качестве заменыvalidate()
,
Приложение: Я пересмотрел ваш пример ниже, чтобы проиллюстрировать некоторые дополнительные моменты:
Не спи на EDT; действительно использовать
javax.swing.Timer
чтобы ускорить анимацию.Override
getPreferredSize()
установить исходную геометрию графического компонента.Профиль для проверки ожидаемого использования кучи.
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://stackru.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();
}
});
}
}