在Java中集中调试

Par*_*rag 11 java swing focus

问题:

我正在尝试在Java Swing应用程序中调试一些与焦点相关的问题.有些时候某些组件似乎正在抓住焦点,我无法弄清楚代码在哪里发生.

我尝试过的:

  • A VetoableChangeListenerKeyboardFocusManager(for focusOwner).这确实为我提供了有关哪些组件丢失并获得焦点的信息,但它无法帮助我确定代码中请求焦点的位置.

  • 一个习惯KeyboardFocusManager.但在这方面我也只能在收到事件时进行干预.到那时,调用的调用栈requestFocus已经丢失.

  • 一个习惯EventQueue.但是我也能够干预dispatchEvent从EDT再次调用的方法.调用堆栈再次丢失(有趣的postEvent(AWTEvent)是没有调用).

题:

我正在寻找的是调用时的调用堆栈requestFocusInWindow.是否有可能获得此信息.也许,如果我可以重新定义用于发布事件的方法EventQueue,那么我可以打印堆栈转储.但是EventQueue.postEvent(AWTEvent)不会被调用.

任何人都可以建议一个解决方案,它可以帮助我在拨打requestFocusrequestFocusInWIndow可能已经拨打电话时获得筹码状态吗?

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

  • 这是应用配置文件的一种优雅方式:http://stackoverflow.com/a/960133/772981 (2认同)

fin*_*nnw 7

他们(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并为您提供堆栈跟踪.