3

この記事があります:


誰かが列を飛び越えました!ときどき、いくつかのスイング イベントがイベント キューで間違った順序で処理されているように見えます (そして、誰かがキューに割り込むときのように私の血が沸騰することはありません) 奇妙な動作を引き起こします。これは、小さなコード スニペットで最もよく示されています。以下のスニペットを読んで、イベントがどのような順序で起こると想像するかを注意深く考えてください。

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
         repaint();
         doSomething();
    }
});

ほとんどの開発者は、repaint() メソッドが doSomething() メソッド呼び出しの前に描画操作を行う結果になると想像するでしょう。ただし、実際にはそうではありません。repaint() を呼び出すと、イベント キューの最後に追加される新しいペイント イベントが作成されます。この新しいペイント イベントは、現在のアクション イベントが完了した後にのみ処理 (ディスパッチ) されます。これは、キュー上の新しいペイント イベントがディスパッチされる前に、doSomething() メソッドが実行されることを意味します。

ここで重要な点は、repaint() を呼び出すと新しいペイント イベントが作成され、それが最後のイベント キューに追加され、すぐには処理されないということです。これは、キューをジャンプするイベントがないことを意味します (そして、私の血液は正しい温度を保つことができます)。

(ソース)


私の質問は、どうすれば Swing にrepaint();BEFOREを実行させることができdoSomething();ますか?

repaint()また、メソッド WITHINへの呼び出しがあった場合、それらは完了doSomething();後にのみ実行されます。途中でexecutindoSomething();を一時停止してから、を投入し、それを完了してから再開する方法はありますか?doSomething();reapaint();doSomething();

これまでに見つけた唯一の解決策はthis(link)ですが、実際には実用的ではありません...

4

2 に答える 2

6

さて、あなたと引用記事の著者は、ここで要点を見逃しています。「repaint」メソッド呼び出しは、次のことを再描画マネージャーに通知するだけです。

  1. 再描画するコンポーネントがあります (「再描画」を呼び出します)
  2. (x、y、w、h)クリップで再描画する必要があります(rectを指定せずに「再描画」を呼び出すと、コンポーネントの境界全体(0、0、w、h)になります)

したがって、同じコンポーネントに対して大量の再描画を 1 つずつ呼び出している場合、再描画がいつ発生するかは問題ではありません。これが、そのような結果呼び出しがマージされる理由でもあります。この例を確認してください:

private static int repaintCount = 0;

public static void main ( String[] args )
{
    final JComponent component = new JComponent ()
    {

        protected void paintComponent ( Graphics g )
        {
            try
            {
                // Simulate heavy painting method (10 milliseconds is more than enough)
                Thread.sleep ( 10 );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace ();
            }

            g.setColor ( Color.BLACK );
            g.drawLine ( 0, 0, getWidth (), getHeight () );

            repaintCount++;
            System.out.println ( repaintCount );
        }
    };
    component.setPreferredSize ( new Dimension ( 200, 200 ) );

    JFrame frame = new JFrame ();
    frame.add ( component );
    frame.pack ();
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setVisible ( true );

    new Thread ( new Runnable ()
    {
        public void run ()
        {
            try
            {
                Thread.sleep ( 1000 );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace ();
            }
            System.out.println ( "Starting repaint calls" );
            for ( int i = 0; i < 100000; i++ )
            {
                component.repaint ();
            }
            System.out.println ( "Finishing repaint calls" );
        }
    } ).start ();
}

これは、表示されるおおよその出力です (コンピューターの速度、Java のバージョン、およびその他の多くの条件によって異なる場合があります)。

1
Starting repaint calls
2
3
4
5
6
Finishing repaint calls
7
8

"1" - フレームが表示されたときの最初の再描画。
"2,3,4..." - 別の非 EDT スレッドからの呼び出しにより、他の 7 回の再描画が発生しました。

「しかし、7回ではなく、100000回の再描画を呼び出しました!」-あなたは言うでしょう。はい、再描画マネージャーは、再描画キューにある類似したものを同時にマージしました。これは、再描画を最適化し、UI 全体を高速化するために行われます。

ところで、EDT から repaint を呼び出す必要はありません。これは、実際の描画を実行せず、将来のためにコンポーネントの更新をキューに入れるだけだからです。それはすでにスレッドセーフな方法です。

要約すると、他のアクションを実行する直前にコンポーネントを再描画する必要がある状況はありません (再描画を引き起こす可能性もあります)。コンポーネントを再描画する必要がある場合は、再描画を呼び出すだけです (可能な場合は指定された四角形を使用)。残りは再描画マネージャーが行います。これは、完全に間違っていて多くの問題を引き起こす可能性のある paint メソッド内に計算を入れない限り、うまく機能します。

于 2013-08-05T15:56:29.317 に答える