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.7 ではなく JDK 1.6 で機能したようです。今実行すると、例外が発生します。java.lang.NoSuchFieldException: PopupFactory_FORCE_HEAVYWEIGHT_POPUP

そのポップアップをJFilechooserの上にとどめるために1.7の解決策がどうなるかはわかりません。

4

1 に答える 1

2

隠された実装の詳細に依存することは、予告なしに変更のリスクを伴います :-) それを行う場合 - 明らかな推奨事項は、実稼働コードでは絶対に決してしないことです - 古いハックが停止した場合、ソースの腸を深く掘り下げる準備をしてください働く。

この特定のケースでは、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 にプロパティを適用するか
于 2013-07-07T09:33:47.097 に答える