1

問題

Swing でダイアログ ボックスを作成します (JRE 6 update 10、Ubuntu Linux)。ユーザーがダイアログ ボックスの使用を終了すると、ダイアログ ボックスは非表示になります。ユーザーが別のフレームのボタンをクリックすると、ボックスのラベルがボタンに応じて変更され、ボックスが再び表示されます。

私が抱えている問題は、プログラム的には逆の順序で呼び出しを行っているにもかかわらず、ラベルが変更される前にボックスが表示されることです。これにより、ボックスが表示され、続いてラベルが変更され、低速のターゲット HW では「グリッチ」に見えます。EDT はフレームsetVisible(true)をラベルsetText(....)の前にスケジュールしているようです。この呼び出しを優先します。setText(....)の後にsetVisible(true)を実行するように EDT をスケジュールする方法はありますか?

コードは EDT で既に実行されているボタン クリックから呼び出されるため、 SwingUtilities.invokeAndWaitを使用できないことに注意してください。invokeLaterメソッドを使用してみましたが、EDT はまだそれを再スケジュールします。

再現するには

IDE で次のコードをデバッグ モードで実行し、「ダイアログ」フレームを表示および非表示にした後、 showButtonのアクション コードを中断します。ラベルのsetText(....)の変更は GUI にすぐには影響しませんが、フレームのsetVisible(true)は影響します。次に、EDT をステップ実行すると、 setTextが最終的に EDT スケジュールのさらに下で発生することがわかります。

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;

public class DemonstrateFramePaintEDTPriority {

static class MyFrame extends JFrame {

    private JFrame frame;
    private JLabel label;
    int i = 0;

    public MyFrame() {
        // Some label strings
        final String string[] = new String[] { "label text one",
                "label 2222222", "3 3 3 3 3 3 3" };

        // Create GUI components.
        frame = new JFrame("Dialog");
        label = new JLabel("no text set on this label yet");
        frame.setSize(500, 200);
        frame.setLayout(new FlowLayout());
        frame.add(label);

        // Add show and hide buttons.
        JButton showButton = new JButton("show dialog");
        showButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // Set the label text - THIS HAPPENS AFTER frame.setVisible
                label.setText(string[i]);

                // Select new label text for next time.
                i++;
                if (i >= string.length) {
                    i = 0;
                }

                // Show dialog - THIS HAPPENS BEFORE label.setText
                frame.setVisible(true);
            }

        });

        JButton hideButton = new JButton("hide dialog");
        hideButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                label.setText("label removed");
                frame.setVisible(false);
            }

        });
        setSize(500, 200);
        setLayout(new FlowLayout());
        add(showButton);
        add(hideButton);
    }
}

public static void main(String[] args) {
    JFrame frame = new MyFrame();
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    frame.setVisible(true);
}
}
4

3 に答える 3

1

これは Linux ペインティングの問題ではないと思います。Windows 7 64 ビット (JDK 1.6.0_18 (早期アクセス)) で問題を再現できます。あなたは答えに非常に近づいています.SwingUtilities.invokeLaterについては知っていますが、必要な場所でそれを使用することは考えていません.

私と一緒に言ってください:

EDT で Swing コンポーネントを初期化および変更する必要があります

そうしないと、悪いことが起こります。この場合、奇妙な再描画動作は、EDT で JFrame とそれに含まれるコンポーネントを作成しなかった結果です。この行を SwingUtilities.invokeLater でラップすると、問題が解決します。

JFrame frame = new MyFrame();

あなたは正しいです - setText は EDT で発生していますが、コンポーネント自体の初期化は EDT で発生していません。これが根本的な原因です。

特定のコードが EDT で発生するかどうか不明な場合は、SwingUtilities.isEventDispatchThread() を使用して調べることができます。

多くの Swing 開発を計画している場合は、Filthy Rich Clientsを読むことを強くお勧めします。今、読んでいる最中です。

于 2009-12-31T17:29:41.717 に答える
0

OS Xで同様の問題が発生しました。私の解決策は次のとおりです。

frame.invalidate()
frame.pack()
frame.setVisible(true)

これは、フレームを表示する前に、メモリ内のフレームを強制的に再描画するように見えます。

于 2009-12-30T20:12:36.030 に答える
0

問題は、ラベル コンポーネントのテキストが変更されていないことではありません。再塗装が予定されているが、まだ行われていないということです。それに、Linux ではウィンドウを開くのが極端に遅くなる傾向があります (ウィンドウ マネージャなどの問題ですか?)。java.awt.EventQueue詳細は思い出せませんが、優先度順のスケジュールです。

JComponent.paintImmediatelyありそうな方法のようです。Swing/AWT 内のアニメーションに関する (良い) テキストを見つけたいと思うかもしれません。(または、ウィンドウマネージャーなしで実行します。)

于 2009-10-13T11:46:25.627 に答える