2

アニメーションを実行するアプリケーションを作成しようとしています。これを行うために、アニメーションが実行される Jpanel のサブクラスを含む Jframe を用意しました。ここに私の2つのクラスがあります:

まず、ここに私のドライバークラスがあります:

import javax.swing.*;

public class Life {
    public static void main(String[] args){
         JFrame game = new JFrame("Life");

         game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         game.setSize(500, 500);

         MainPanel mainPanel = new MainPanel();
         game.setContentPane(mainPanel);


         game.setVisible(true);

     }
 }

次に、Jpanel のサブクラスを次に示します。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MainPanel extends JPanel implements ActionListener{
    int i = 0;
    int j = 0;
    public MainPanel(){
        super();
    }

    public void paintComponent(Graphics g){
        j++;
        g.drawLine(10,10, 20 + i, 20 + i);
        Timer t = new Timer(1000, this);
        t.start();
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        i++;
        repaint();
    }
}

actionPreformed が呼び出されるたびに変数 i がインクリメントされ、paintComponent が呼び出されるたびに変数 j が呼び出されることに注意してください。結局、i が j よりもはるかに大きくなり始め、paintComponent によって描画された線がどんどん速く伸びているように見えます。

ここに私の質問があります:

  • なぜこれが起こるのですか?
  • 1000ミリ秒ごとに線が再描画されるように同期するにはどうすればよいですか?
  • 私がやろうとしていることを考えると、私のアプローチは間違っていますか? 私は違うことをするべきですか?

前もって感謝します。

4

3 に答える 3

4

paintComponentメソッド内から Swing Timer を開始しないでください。

このメソッドは、あなたの絵だけを描き、絵だけを描くべきです。プログラムロジックを一切含めないでくださいこのメソッドがいつ呼び出されるか、呼び出されるかどうか、どのくらいの頻度で呼び出されるかを予測できないため、このメソッドに対する制御は非常に限られていることを理解してください。自分で呼び出すこともできませんし、それを介して呼び出すことを提案repaint()したときに実際に呼び出されることを保証することもできません。

また、オブジェクトの作成やファイルの読み込みなど、速度を低下させるものはすべて、GUI の認識される応答性を低下させるため、このメソッドは可能な限り高速である必要があります。

解決策は、プログラム ロジックをそのメソッドから分離し、コンストラクターなどのより優れたメソッドに分離することです。繰り返しコードは Swing Timer にある必要があります。

編集:
あなたは次のように述べています:

私は物事をテストするためにそれをしました。もう 1 つの質問: paintComponent、または paintComponent 内の作業が依存するスレッドが、その作業を行うのに 1000 ミリ秒 (またはそれが何であれ) より長くかかる場合はどうなりますか? 私が考えることができる唯一のことは、アニメーションが次のステップに到達するのを待つのではなく、それまでのアニメーションの進行状況を paintComponent にペイントさせることです (それが理にかなっている場合)。考え?

それほど長く、または数十ミリ秒かかるコードを paintComponent に含めるべきではありません。そのようなことが起こる可能性がある場合は、バックグラウンド スレッドで BufferedImage に描画を行い、Swing イベント スレッドでメソッドを使用して PaintComponent メソッドで BufferedImage を表示しGraphics#drawImage(...)ます。

于 2012-09-30T05:27:09.907 に答える
3

@HFoE の本質的な洞察へのいくつかのマイナーな追加:

改訂されたコード:

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Life {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            private final JTabbedPane jtp = new JTabbedPane();

            @Override
            public void run() {
                JFrame game = new JFrame("Life");
                game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                MainPanel mainPanel = new MainPanel();
                game.setContentPane(mainPanel);
                game.pack();
                game.setVisible(true);
                mainPanel.start();
            }
        });
    }

    private static class MainPanel extends JPanel implements ActionListener {

        private Timer t = new Timer(100, this);
        private int i;
        private int j;

        @Override
        public void paintComponent(Graphics g) {
            g.drawLine(10, 10, 20 + i, 20 + i);
        }

        public void start() {
            t.start();
        }

        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            i++;
            repaint();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(500, 500);
        }
    }
}
于 2012-09-30T05:42:08.977 に答える
1

ATimerはデフォルトで実行を続けます。呼び出したときだけsetRepeats( false )停止します。

したがって、次の行

Timer t = new Timer(1000, this);
t.start();

paintComponentメソッドの. _ Timer_ij

解決策はもちろんTimer、メソッドの外側に移動し、1 つのインスタンスpaintComponentに固執することです。Timer

さらなる発言(他の人には言われなかったので、彼らの非常に有益なアドバイスを繰り返すつもりはありません):

  • paintComponentメソッドを呼び出さずにメソッドをsuperオーバーライドしない
  • インターフェイスを公開しないでくださいActionListenerActionListener内部で使用するだけです
于 2012-09-30T07:52:54.007 に答える