隠された実装の詳細に依存することは、予告なしに変更のリスクを伴います :-) それを行う場合 - 明らかな推奨事項は、実稼働コードでは絶対に決してしないことです - 古いハックが停止した場合、ソースの腸を深く掘り下げる準備をしてください働く。
この特定のケースでは、jdk6 から jdk7 への変更は、キーを PopupFactory のプライベート フィールドから package-private enum 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 が lightWeight ポップアップが適切であると判断すると、呼び出し元が実際に 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 ...
}
修正する場所は、heavyWeight ポップアップが必要かどうかに関係なく、意思決定チェーンの上位になります。PopupFactory.getPopupType(Component ..)
これは残念ながらプライベートであるため、サブクラス化して動作させる方法はありません。実際、それを安全に修正する方法はまったくありません。これまでに見たすべてのアプローチでは、ダーティになり、リフレクションを介してパッケージ/プライベート フィールドまたはメソッドにアクセスする必要があります。これは、セキュリティが制限されたコンテキストでは失敗します。
個人的には、ダーティ ファクトリを好む傾向があります (スーパーの setPopupType にアクセスif !isDecending(invoker, layeredPane)
して、ダーティな clientProperty よりもヘビーウェイトを強制するのには次の 2 つの理由があります。
- それはjdkバージョンに依存しません
- 単一の場所で問題を修正します: カスタム popupFactory を 1 回インストールするか、各 glassPane にプロパティを適用するか