如何防止任务栏中的JFrame警报效果
How to prevent JFrame alert effect in taskbar
我是一名 Java SE 开发者,我们的程序只在 windows OS 运行,程序需要打开文件并显示在 UI ,我们在 JInternalFrame 中显示文件内容,并在文件打开时始终 setselected(true)
。
最近我们将我们的程序从 JRE1.6.0_41
更新到 JRE1.8.0_144
以解决一个主要错误,并注意到在 JRE1.8 中。0_144 当我们的程序 (JFrame) 未获得焦点并且在 JFrame 的 JInternalFrame 上调用 setselected(true)
,JFrame 会在任务栏中闪烁,而 JRE1.6.0_41 不会。有些客户认为这很烦人,因为他们必须经常打开文件。
所以问题是:
(1) 我可以关闭该功能吗?
(2) 如果我不能,有没有其他方法可以避免闪烁效果,同时我仍然可以在 JInternalFrame 上 setSelected()
?
如果我没解释好,下面是示例代码演示提醒效果,请运行程序最小化到任务栏切换到另一个 window,然后它应该会闪烁。我在Windows 7, Windows 8.1 和 Windows 10 上测试过,它们都具有相同的警报效果。
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
createFrame(); //create first "window"
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
// Add new frame every second
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
更新 01
这是根据@camickr 建议添加 KeyboardFocusManager
和 WindowListener
的更新代码,当 JFrame 获得焦点或 JFrame 获得焦点时,仅在 JInternalFrame 上添加 setselected(true)
,但警报仍然存在如果您频繁切换 windows 会发生这种情况,我认为这是因为 JFrame 在 JInternalFrame 上的 setselected(true)
之前失去了焦点。也许有改进代码的方法?
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
createFrame(); //create first "window"
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
/********************************************* update codes *********************************************/
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is focus owner
if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != null &&
SwingUtilities.isDescendingFrom(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), this)) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
更新 02
尝试使用 getGlobalActiveWindow()
和 windowActivated/windowDeactivated
,如果频繁切换 windows,警报仍然会发生。下面是测试代码:
getGlobalActiveWindow()
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
/********************************************* update codes *********************************************/
private MyDefaultKeyboardFocusManager m_MyKeyboardFocusManager = new MyDefaultKeyboardFocusManager();
public class MyDefaultKeyboardFocusManager extends DefaultKeyboardFocusManager {
//The method is protected, need to add public method to call it
public Window myGetGlobalActiveWindow() {
return this.getGlobalActiveWindow();
}
}
/********************************************* update codes *********************************************/
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
KeyboardFocusManager.setCurrentKeyboardFocusManager(m_MyKeyboardFocusManager);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is active
if (m_MyKeyboardFocusManager.myGetGlobalActiveWindow() == this) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
windowActivated/windowDeactivated
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
/********************************************* update codes *********************************************/
private boolean m_bIsFrameActive = false;
/********************************************* update codes *********************************************/
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
m_bIsFrameActive = true;
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
m_bIsFrameActive = false;
}
});
/********************************************* update codes *********************************************/
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is active
if (m_bIsFrameActive) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
we display the file contents in JInternalFrame and always setselected(true) when the file is opened.
不知道如何停止任务栏上的闪烁,但您可以通过不自动调用 setSelected() 来更改行为。
例如,您可以:
仅在框架处于活动状态时调用 setSelected()
window。您可以使用 KeyboardFocusManager
检查 window 是否聚焦。
向框架添加一个WindowListener
并处理windowActivated
事件以在最近打开的内部框架上调用setSelected()方法。
编辑:
run the program and minimize it into taskbar,
首先我要澄清的是,如果我将框架最小化到任务栏,图标闪烁是没有问题的。
仅当我单击另一个应用程序时图标才会闪烁,并且登录框架在打开时失去焦点。
the alert still happen if you switch windows frequently,
我将您的代码更改为:
//if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != null &&
// SwingUtilities.isDescendingFrom(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), this)) {
if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() == this)
{
try
{
System.out.println("active");
frame.setSelected(true);
}
catch (java.beans.PropertyVetoException e) {}
}
else
{
System.out.println("not");
// frame = m_InternalFrameWaitSelected;
m_InternalFrameWaitSelected = frame;
}
我注意到有时 "active" 会显示在另一个 window 中,即使它正在玩游戏。所以出于某种原因,KeyboardFocusManager 返回了一个不正确的值(或者我不明白该方法应该如何工作?)
所以也许你可以尝试使用 getGlobalActiveWindow()
方法看看它是否有任何不同。
如果没有,那么我的下一个建议是忘记 KeyboardFocusManager 并管理一个布尔变量,该变量指示您的框架是否处于活动状态。因此,您可以在 windowActivated 中设置变量 true,在 windowDeactivated 中设置 false。然后在创建框架时测试此变量。
另请注意我是如何更改以下代码的:
// frame = m_InternalFrameWaitSelected;
m_InternalFrameWaitSelected = frame;
如果有人有兴趣解答,我追查JInternalFrame中的源代码后,发现闪烁效果是由调用setSelected(true)
时的requestFocus()
引起的,所以我决定继承JInternalFrame,将requestFocus()
替换为requestFocusInWindow()
,现在闪烁效果消失了。
这是我对这个问题的最终解决方案:
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import sun.swing.SwingUtilities2;
import javax.swing.InternalFrameFocusTraversalPolicy;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
/**
* This class is created to handle problem that program would blink in taskbar
* when call setSelected(true) on JInternalFrame base on JRE1.8.0_144, the only different content is
* use lastFocusOwner.requestFocusInWindow(); instead of lastFocusOwner.requestFocus();
* in method restoreSubcomponentFocus()
*
*/
public class MyInternalFrame extends JInternalFrame {
private Component lastFocusOwner;
private final String BASE_JRE_VERSION = "1.8.0_144";
public MyInternalFrame() {
_checkJavaVersion();
}
public MyInternalFrame(String title) {
super(title);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable) {
super(title, resizable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable) {
super(title, resizable, closable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable) {
super(title, resizable, closable, maximizable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable) {
super(title, resizable, closable, maximizable, iconifiable);
_checkJavaVersion();
}
private void _checkJavaVersion() {
if (!BASE_JRE_VERSION.equals(System.getProperty("java.version")))
System.err.println(String.format("%s is not compatible with current Java runtime version : %s ", this.getClass().toString(), System.getProperty("java.version")));
}
@Override
public void restoreSubcomponentFocus() {
if (isIcon()) {
SwingUtilities2.compositeRequestFocus(getDesktopIcon());
}
else {
Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
if ((component == null) || !SwingUtilities.isDescendingFrom(component, this)) {
// FocusPropertyChangeListener will eventually update
// lastFocusOwner. As focus requests are asynchronous
// lastFocusOwner may be accessed before it has been correctly
// updated. To avoid any problems, lastFocusOwner is immediately
// set, assuming the request will succeed.
setLastFocusOwner(getMostRecentFocusOwner());
if (lastFocusOwner == null) {
// Make sure focus is restored somewhere, so that
// we don't leave a focused component in another frame while
// this frame is selected.
setLastFocusOwner(getContentPane());
}
lastFocusOwner.requestFocusInWindow();
}
}
}
private void setLastFocusOwner(Component component) {
lastFocusOwner = component;
}
@Override
public Component getMostRecentFocusOwner() {
if (isSelected()) {
return getFocusOwner();
}
if (lastFocusOwner != null) {
return lastFocusOwner;
}
FocusTraversalPolicy policy = getFocusTraversalPolicy();
if (policy instanceof InternalFrameFocusTraversalPolicy) {
return ((InternalFrameFocusTraversalPolicy)policy).
getInitialComponent(this);
}
Component toFocus = policy.getDefaultComponent(this);
if (toFocus != null) {
return toFocus;
}
return getContentPane();
}
@Override
public Component getFocusOwner() {
if (isSelected()) {
return lastFocusOwner;
}
return null;
}
}
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new MyInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
我是一名 Java SE 开发者,我们的程序只在 windows OS 运行,程序需要打开文件并显示在 UI ,我们在 JInternalFrame 中显示文件内容,并在文件打开时始终 setselected(true)
。
最近我们将我们的程序从 JRE1.6.0_41
更新到 JRE1.8.0_144
以解决一个主要错误,并注意到在 JRE1.8 中。0_144 当我们的程序 (JFrame) 未获得焦点并且在 JFrame 的 JInternalFrame 上调用 setselected(true)
,JFrame 会在任务栏中闪烁,而 JRE1.6.0_41 不会。有些客户认为这很烦人,因为他们必须经常打开文件。
所以问题是:
(1) 我可以关闭该功能吗?
(2) 如果我不能,有没有其他方法可以避免闪烁效果,同时我仍然可以在 JInternalFrame 上 setSelected()
?
如果我没解释好,下面是示例代码演示提醒效果,请运行程序最小化到任务栏切换到另一个 window,然后它应该会闪烁。我在Windows 7, Windows 8.1 和 Windows 10 上测试过,它们都具有相同的警报效果。
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
createFrame(); //create first "window"
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
// Add new frame every second
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
更新 01
这是根据@camickr 建议添加 KeyboardFocusManager
和 WindowListener
的更新代码,当 JFrame 获得焦点或 JFrame 获得焦点时,仅在 JInternalFrame 上添加 setselected(true)
,但警报仍然存在如果您频繁切换 windows 会发生这种情况,我认为这是因为 JFrame 在 JInternalFrame 上的 setselected(true)
之前失去了焦点。也许有改进代码的方法?
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
createFrame(); //create first "window"
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
/********************************************* update codes *********************************************/
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is focus owner
if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != null &&
SwingUtilities.isDescendingFrom(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), this)) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
更新 02
尝试使用 getGlobalActiveWindow()
和 windowActivated/windowDeactivated
,如果频繁切换 windows,警报仍然会发生。下面是测试代码:
getGlobalActiveWindow()
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
/********************************************* update codes *********************************************/
private MyDefaultKeyboardFocusManager m_MyKeyboardFocusManager = new MyDefaultKeyboardFocusManager();
public class MyDefaultKeyboardFocusManager extends DefaultKeyboardFocusManager {
//The method is protected, need to add public method to call it
public Window myGetGlobalActiveWindow() {
return this.getGlobalActiveWindow();
}
}
/********************************************* update codes *********************************************/
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
KeyboardFocusManager.setCurrentKeyboardFocusManager(m_MyKeyboardFocusManager);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is active
if (m_MyKeyboardFocusManager.myGetGlobalActiveWindow() == this) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
windowActivated/windowDeactivated
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
private JInternalFrame m_InternalFrameWaitSelected = null;
/********************************************* update codes *********************************************/
private boolean m_bIsFrameActive = false;
/********************************************* update codes *********************************************/
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
/********************************************* update codes *********************************************/
//Add window listener to set last JInternalframe selected when JFrame activated
this.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
m_bIsFrameActive = true;
if (m_InternalFrameWaitSelected != null) {
try {
m_InternalFrameWaitSelected.setSelected(true);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
m_InternalFrameWaitSelected = null;
}
}
@Override
public void windowDeactivated(WindowEvent e) {
m_bIsFrameActive = false;
}
});
/********************************************* update codes *********************************************/
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new JInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
/********************************************* update codes *********************************************/
// Only setselect(true) when JFrame is active
if (m_bIsFrameActive) {
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
m_InternalFrameWaitSelected = frame;
}
/********************************************* update codes *********************************************/
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
we display the file contents in JInternalFrame and always setselected(true) when the file is opened.
不知道如何停止任务栏上的闪烁,但您可以通过不自动调用 setSelected() 来更改行为。
例如,您可以:
仅在框架处于活动状态时调用
setSelected()
window。您可以使用KeyboardFocusManager
检查 window 是否聚焦。向框架添加一个
WindowListener
并处理windowActivated
事件以在最近打开的内部框架上调用setSelected()方法。
编辑:
run the program and minimize it into taskbar,
首先我要澄清的是,如果我将框架最小化到任务栏,图标闪烁是没有问题的。
仅当我单击另一个应用程序时图标才会闪烁,并且登录框架在打开时失去焦点。
the alert still happen if you switch windows frequently,
我将您的代码更改为:
//if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != null &&
// SwingUtilities.isDescendingFrom(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), this)) {
if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() == this)
{
try
{
System.out.println("active");
frame.setSelected(true);
}
catch (java.beans.PropertyVetoException e) {}
}
else
{
System.out.println("not");
// frame = m_InternalFrameWaitSelected;
m_InternalFrameWaitSelected = frame;
}
我注意到有时 "active" 会显示在另一个 window 中,即使它正在玩游戏。所以出于某种原因,KeyboardFocusManager 返回了一个不正确的值(或者我不明白该方法应该如何工作?)
所以也许你可以尝试使用 getGlobalActiveWindow()
方法看看它是否有任何不同。
如果没有,那么我的下一个建议是忘记 KeyboardFocusManager 并管理一个布尔变量,该变量指示您的框架是否处于活动状态。因此,您可以在 windowActivated 中设置变量 true,在 windowDeactivated 中设置 false。然后在创建框架时测试此变量。
另请注意我是如何更改以下代码的:
// frame = m_InternalFrameWaitSelected;
m_InternalFrameWaitSelected = frame;
如果有人有兴趣解答,我追查JInternalFrame中的源代码后,发现闪烁效果是由调用setSelected(true)
时的requestFocus()
引起的,所以我决定继承JInternalFrame,将requestFocus()
替换为requestFocusInWindow()
,现在闪烁效果消失了。
这是我对这个问题的最终解决方案:
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import sun.swing.SwingUtilities2;
import javax.swing.InternalFrameFocusTraversalPolicy;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
/*
* Copy and modified by Oracle sample code "InternalFrameDemo.java"
*/
public class Login extends JFrame {
JDesktopPane desktop;
private int m_iFrameCounter = 0;
/**
* This class is created to handle problem that program would blink in taskbar
* when call setSelected(true) on JInternalFrame base on JRE1.8.0_144, the only different content is
* use lastFocusOwner.requestFocusInWindow(); instead of lastFocusOwner.requestFocus();
* in method restoreSubcomponentFocus()
*
*/
public class MyInternalFrame extends JInternalFrame {
private Component lastFocusOwner;
private final String BASE_JRE_VERSION = "1.8.0_144";
public MyInternalFrame() {
_checkJavaVersion();
}
public MyInternalFrame(String title) {
super(title);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable) {
super(title, resizable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable) {
super(title, resizable, closable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable) {
super(title, resizable, closable, maximizable);
_checkJavaVersion();
}
public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable) {
super(title, resizable, closable, maximizable, iconifiable);
_checkJavaVersion();
}
private void _checkJavaVersion() {
if (!BASE_JRE_VERSION.equals(System.getProperty("java.version")))
System.err.println(String.format("%s is not compatible with current Java runtime version : %s ", this.getClass().toString(), System.getProperty("java.version")));
}
@Override
public void restoreSubcomponentFocus() {
if (isIcon()) {
SwingUtilities2.compositeRequestFocus(getDesktopIcon());
}
else {
Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
if ((component == null) || !SwingUtilities.isDescendingFrom(component, this)) {
// FocusPropertyChangeListener will eventually update
// lastFocusOwner. As focus requests are asynchronous
// lastFocusOwner may be accessed before it has been correctly
// updated. To avoid any problems, lastFocusOwner is immediately
// set, assuming the request will succeed.
setLastFocusOwner(getMostRecentFocusOwner());
if (lastFocusOwner == null) {
// Make sure focus is restored somewhere, so that
// we don't leave a focused component in another frame while
// this frame is selected.
setLastFocusOwner(getContentPane());
}
lastFocusOwner.requestFocusInWindow();
}
}
}
private void setLastFocusOwner(Component component) {
lastFocusOwner = component;
}
@Override
public Component getMostRecentFocusOwner() {
if (isSelected()) {
return getFocusOwner();
}
if (lastFocusOwner != null) {
return lastFocusOwner;
}
FocusTraversalPolicy policy = getFocusTraversalPolicy();
if (policy instanceof InternalFrameFocusTraversalPolicy) {
return ((InternalFrameFocusTraversalPolicy)policy).
getInitialComponent(this);
}
Component toFocus = policy.getDefaultComponent(this);
if (toFocus != null) {
return toFocus;
}
return getContentPane();
}
@Override
public Component getFocusOwner() {
if (isSelected()) {
return lastFocusOwner;
}
return null;
}
}
public Login() {
super("InternalFrameDemo");
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset*2,
screenSize.height - inset*2);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
setContentPane(desktop);
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
// Add new frame every 50 millisecond
Thread addFrameThread = new Thread() {
@Override
public void run() {
while(true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createFrame();
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
addFrameThread.start();
}
//Create a new internal frame.
protected void createFrame() {
JInternalFrame frame = new MyInternalFrame();
frame.setTitle("" + m_iFrameCounter);
frame.setSize(100, 100);
frame.setLocation(0, 0);
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
frame.moveToFront();
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
m_iFrameCounter++;
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
Login frame = new Login();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}