4

問題

スタンドアロンの Swing アプリケーションは、いくつかの特定のイベント (ボタンのクリックなど) でモーダル JDialogを表示します。ダイアログには、他の Swing コンポーネント (JLabels、JButtons、...) が含まれています。JDialog.setBounds(...)呼び出すメソッドを介してその次元を明示的に設定するのではなくJDialog.pack()(またはJOptionPaneshowDialog(...)メソッドが暗黙的に呼び出します)。メソッドによって計算されるサイズpack()は常に一定です (例: 300x100 px)。残念ながら、表示されるダイアログの実際のサイズが 1x1 px ( JDialog.getSize().equals(new Dimension(1, 1))) になる場合があります。

JDialog の初期化

アプリケーションで sを初期化する方法は 2 つありJDialogます。両方の初期化メソッドが常に EventDispatchThread から呼び出されることを確認しました。

最初の方法

ADialogのサブクラスであるインスタンスを作成するだけですJDialog。以下は、初期化手順の抜粋です。

ADialog dialog = new ADialog();
dialog.setContentPane(content);
dialog.setVisible(true);

これが私たちのADialog実装です:

public class ADialog extends JDialog implements ComponentListener {

    public JfosDialog(Frame owner) {
        super(owner);
        init();
    }

    private void init() {
        super.addComponentListener(this);
    }

    @Override
    public void componentShown(ComponentEvent e) {
        // Calling pack() at this place is really weird, but we
        // have to do it since some subclasses put their
        // content to dialog in overriden componentShown().
        pack();
    }

    @Override
    public void componentMoved(ComponentEvent e) { /** not interested */ }

    @Override
    public void componentResized(ComponentEvent e) { /** not interested */ }
}

2番目の方法

public JOptionPane showDialog(...) {
    JOptionPane jop = new JOptionPane(message, msgType, option, null, textMessages);
    JDialog dialog = jop.createDialog(owner, titleMsg);
    setDialogTraversal(dialog);
    dialog.setVisible(true);
    dialog.dispose();
    return jop;
}

再現性

環境

  • Ubuntu 12.04.、JRE 1.6 / JRE 1.7 / OpenJDK 1.6
  • Windows XP、JRE 1.6.u16

最初の初期化方法で問題を再現する

ダイアログを表示してから非表示にするのを N 回 (0 < N < 1000) 行い、かつてダイアログのサイズが 1x1 (スレッドの問題、競合状態の兆候) でした。この問題の性質は非常に確率論的であるため、ループ内でダイアログを表示および非表示にする単純なjava.awt.Robotスクリプトを作成しました。手動で行うよりも快適です。

2 番目の初期化方法で問題を再現する

手順は最初の方法と同じです。ダイアログを表示してから N 回非表示にします。残念ながら、開発環境で再現することはできませんが、実稼働 PC では非常に簡単に再現できます (開発環境とは異なる CPU があり、システム負荷を永続的に作成するウイルス対策がインストールされているなど)。

これまでのところ、ある種のテスト/サンプル プロジェクトで問題を再現することはできません。これは、アプリケーションに問題があることを示唆している可能性があります。ただし、問題は Swing のネイティブ コードのどこかにあるようです (「トレース」セクションを参照)。

トレース

64 ビット OpenJDK 1.6.0_24 での問題の原因を追跡しました。JDialogの次元がによってXConfigureEvent起動されていることがわかりましたXToolkitXToolkit.run(boolean)イベントは、ネイティブ メソッド呼び出しから returnig 後のメソッドのイベント ループで構築されます。

XToolkit簡単にするために、トレースの結果を使用して のイベント ループ メカニズムを示すコード スニペットのみをここに投稿します。また、完全なソース コードはこちらでご覧いただけます

public class XToolkit ... {
    ...
    public void run(boolean loop) {
        XEvent ev = new XEvent();
        while(true) {
            awtLock();
            try {
                if (loop == SECONDARY_LOOP) {
                    ...
                } else {
                    ...

                    // ===========================================
                    // The following invocation of native method sometimes
                    // updates ev object in a way that ev.get_type() method returns value 22
                    // indicating that the event's type is XConfigureEvent.
                    // In such case, as I mentioned in text above, the value of
                    // ev.get_xconfigure().get_width() / .get_height()
                    // is sometimes 1.

                    XlibWrapper.XNextEvent(getDisplay(),ev.pData); // <-----

                    // ===========================================
                }
                ...
                // The XConfigureEvent with get_width() == 1 and
                // get_heigth() == 1 is dispatcher here:
                dispatchEvent(ev); // <-----
                ...
            } catch (...) {
                ...
            }
        }
    }
    ...
}

このバグを修正する/より深く追跡する/より確実に再現する方法について何かアイデアはありますか?

このバグは本当に苦痛なので、アイデアをいただければ幸いです。

4

1 に答える 1

6

pack() は、コンポーネントの優先 (またはレイアウトマネージャーに応じた最小サイズ) を尊重する必要があります。

ダイアログのコンポーネントの最小サイズと推奨サイズを明示的に設定して、問題が解決するかどうかを確認してください。

競合状態に関する限り、コードの初期化が正しくない可能性もあります。ほとんどのコンポーネントは、レイアウト マネージャーが優先サイズ (または最小サイズ) を照会すると、コンテンツ (JLabel のテキストなど) を調整します。そのため、コンポーネントにデータを追加するコードが常に同じ順序で実行されるとは限りません。ダイアログとそれに含まれるすべてのコンポーネントを AWT スレッドで作成したことを確認しましたか?

于 2012-05-31T22:10:21.257 に答える