问题:
我正在尝试在Java Swing应用程序中调试一些与焦点相关的问题。有时候,某些组件似乎引起了人们的关注,而我无法弄清楚这种情况在代码中发生了什么。
我尝试过的是:

  • 带有VetoableChangeListenerKeyboardFocusManager(用于focusOwner)。这的确为我提供了有关哪些组件丢失和获得焦点的信息,但并不能帮助我指出在代码中请求焦点的位置。
  • 自定义KeyboardFocusManager。但是在那方面,我也只能在事件发生时进行干预。到那时,对requestFocus的调用的调用堆栈已经丢失。
  • 自定义EventQueue。但是我也可以干预EDT再次调用的dispatchEvent方法。再次,调用堆栈丢失了(有趣的是,没有调用postEvent(AWTEvent))。

  • 问题:
    我正在寻找的是对requestFocusInWindow进行调用时的调用堆栈。是否有可能获得此信息。也许,如果我可以在EventQueue中重新定义用于发布事件的方法,那么我可以打印堆栈转储。但是,EventQueue.postEvent(AWTEvent)不会被调用。
    有人可以提出一种解决方案,当对requestFocusrequestFocusInWIndow进行了调用时,该解决方案将帮助我获取堆栈的状态?

    最佳答案

    看来他们(Sun)确实不希望您这样做。乍一看,在该路径中似乎没有任何可以轻松覆盖的虚拟方法,在EventQueue中没有使用(postEvent仅用于invokeLater和来自应用程序代码的合成事件),也没有在KeyboardFocusManager中使用(如您所知,稍后会在分派(dispatch)循环中调用可覆盖的方法。)

    幸运的是,如果您使用的是Sun JRE,则可以在其中插入代码,但是它并不漂亮:
    Component.requestFocus()调用静态KeyboardFocusManager.setMostRecentFocusOwner(Component),后者会更新称为Map的私有(private)静态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);
    

    这将捕获对requestFocus的调用,并为您提供堆栈跟踪。

    09-26 03:53