我正在尝试在Java Swing应用程序中调试一些与焦点相关的问题.有些时候某些组件似乎正在抓住焦点,我无法弄清楚代码在哪里发生.
A VetoableChangeListener与KeyboardFocusManager(for focusOwner).这确实为我提供了有关哪些组件丢失并获得焦点的信息,但它无法帮助我确定代码中请求焦点的位置.
一个习惯KeyboardFocusManager.但在这方面我也只能在收到事件时进行干预.到那时,调用的调用栈requestFocus已经丢失.
一个习惯EventQueue.但是我也能够干预dispatchEvent从EDT再次调用的方法.调用堆栈再次丢失(有趣的postEvent(AWTEvent)是没有调用).
我正在寻找的是调用时的调用堆栈requestFocusInWindow.是否有可能获得此信息.也许,如果我可以重新定义用于发布事件的方法EventQueue,那么我可以打印堆栈转储.但是EventQueue.postEvent(AWTEvent)不会被调用.
任何人都可以建议一个解决方案,它可以帮助我在拨打requestFocus或requestFocusInWIndow可能已经拨打电话时获得筹码状态吗?
gb9*_*b96 14
我遇到了这个优雅的解决你的问题,不给你调用堆栈但并告诉你哪些类已经抓住重点.
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
// ...
private static void enableFocusLogging() {
// Obtain a reference to the logger
Logger focusLog = Logger.getLogger("java.awt.focus.Component");
// The logger should log all messages
focusLog.setLevel(Level.ALL);
// Create a new handler
ConsoleHandler handler = new ConsoleHandler();
// The handler must handle all messages
handler.setLevel(Level.ALL);
// Add the handler to the logger
focusLog.addHandler(handler);
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以通过更改全局JRE logging.properties(或应用程序的自定义logging.properties文件)来实现此目的.通过这种方式,您可以跟踪AWT焦点事件,而无需源代码或编译器.最后2行是必需的补充:
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
handlers= java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# Log AWT Focus Events
java.util.logging.ConsoleHandler.level = FINEST
java.awt.focus.Component.level = FINEST
Run Code Online (Sandbox Code Playgroud)
另一个生成有关焦点事件的有用信息的记录器名为java.awt.focus.DefaultKeyboardFocusManager
他们(Sun)似乎真的不希望你这样做.乍一看,在该路径中似乎没有任何可以轻易覆盖的虚拟方法,而不是EventQueue(postEvent仅用于invokeLater和合成应用程序代码中的事件),也没有KeyboardFocusManager(正如您所发现的那样,可覆盖的方法被调用)稍后从调度循环.)
幸运的是,如果你使用的是Sun JRE,还有就是你可以插入代码的地方,但它并不漂亮:
Component.requestFocus()调用static KeyboardFocusManager.setMostRecentFocusOwner(Component),它更新一个Map被调用的私有静态mostRecentFocusOwners.
因此,如果您可以Map使用反射访问该静态,则可以使用Map跟踪对其put方法的调用的转发来替换它:
import com.google.common.collect.ForwardingMap;
// ...
Field mrfoField = KeyboardFocusManager.class.getDeclaredField("mostRecentFocusOwners");
mrfoField.setAccessible(true);
final Map delegate = (Map) mrfoField.get(null);
Map mrfo = new ForwardingMap() {
public Object put(Object key, Object value) {
new Throwable().printStackTrace();
return super.put(key, value);
}
protected Map delegate() {
return delegate;
}
};
mrfoField.set(null, mrfo);
Run Code Online (Sandbox Code Playgroud)
这将捕获调用requestFocus并为您提供堆栈跟踪.