1

我有一个JFilechooser放置在JFrame玻璃窗格的顶部。当我选择“查看”弹出窗口时,它似乎出现在JFilechooser. 不久前,我使用在网上找到的解决方案修复了这个问题,以强制弹出窗口成为重量级组件:

    try {
        field = PopupFactory.class.getDeclaredField(
                        "forceHeavyWeightPopupKey");
        field.setAccessible(true);
        fileChooser.putClientProperty(field.get(null), true);
    } catch (NoSuchFieldException ex) {
        java.util.logging.Logger.getLogger(LibraryUI.class.getName()).
                log(Level.SEVERE, null, ex);
    } catch (SecurityException ex) {
        java.util.logging.Logger.getLogger(LibraryUI.class.getName()).
                log(Level.SEVERE, null, ex);
    }

似乎该解决方案适用于 JDK 1.6 而不是 1.7。当我现在运行它时,我得到了异常:java.lang.NoSuchFieldException: PopupFactory_FORCE_HEAVYWEIGHT_POPUP

我不确定 1.7 的解决方案是使弹出窗口保持在 JFilechooser 之上。

4

1 回答 1

2

依赖隐藏的实现细节可能会在不通知的情况下进行更改 :-) 如果您这样做 - 显而易见的建议是永远不要在生产代码中使用 - 如果旧的黑客停止,请准备好深入挖掘源代码在职的。

在这种特殊情况下,从 jdk6 到 jdk7 的更改是将密钥从 PopupFactory 中的私有字段移动到包私有枚举 ClientPropertyKey 的值。所以你必须将反射代码调整为:

public void forceHeavyWeight(JCompoennt fileChooser) {
    try {
        String name = "javax.swing.ClientPropertyKey";
        Class<?> keyClazz = Class.forName(name);
        Field field = keyClazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP");
        field.setAccessible(true);
        Object fieldValue = field.get(null);
        fileChooser.putClientProperty(fieldValue, true);
    } catch (Exception ex) {
        // doesn't really matter what we do here, lost anyway ;-)
        logSomehow(ex);
    }
}

更新

经过一番挖掘,我认为行为(在添加到 glassPane 的组件下以 z 顺序显示弹出窗口)是一个错误。设置为 glassPane 中组件的工具提示也存在相同的错误行为。

技术上的原因是,一旦 PopupFactory 确定轻量级弹出窗口就可以了,它就会将其插入到调用者的顶级祖先的 layeredPane 中,无论调用者是否实际上是 layeredPane 层次结构的一部分。PopupFactoryLightWeightPopup.show() 中的确切位置:

// suitable parent of the invoker, to add the popup
Container parent = null;

if (owner != null) {
    parent = (owner instanceof Container? (Container)owner : owner.getParent());
}

// Try to find a JLayeredPane and Window to add
for (Container p = parent; p != null; p = p.getParent()) {
    if (p instanceof JRootPane) {
        if (p.getParent() instanceof JInternalFrame) {
        // Continue, so that if there is a higher JRootPane, we'll
        // pick it up.
            continue;
        }

        parent = ((JRootPane)p).getLayeredPane();
        // the implied assumption for correct visuals is that the assert below passes
        // would fail if the invoker is
        // located in the glassPane hierarchy
        assertTrue(SwingUtilities.isDescendingFrom(owner, parent);
    } else ....
    }
    // with the failing assumption above, the popup is inserted below its invoker 
    if (parent instanceof JLayeredPane) {
        parent.add(component, JLayeredPane.POPUP_LAYER, 0);
    } else ...   
}

无论是否需要一个重量级弹出窗口,在决策链中修复的位置都将位于更高的位置:最好的PopupFactory.getPopupType(Component ..)(那是上面的 hack 是有效的),不幸的是它是私有的,所以没有办法子类化并使其行为。事实上,根本没有办法安全地修复它,到目前为止我看到的所有方法都需要变脏并通过反射访问包/私有字段或方法——这在安全受限的上下文中会失败。

就个人而言,我更喜欢脏工厂(访问 super 的 setPopupType 以强制重量级if !isDecending(invoker, layeredPane)超过脏 clientProperty 有两个原因:

  • 它独立于 jdk 版本
  • 它在一个地方解决了问题:安装自定义 popupFactory 一次与为每个 glassPane 应用属性
于 2013-07-07T09:33:47.097 回答