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