0

私が時間をかけて取り組んだのは、停止してリセットするまでの時間を計測するストップウォッチやクロノメーターのように、ユーザーがスタート ボタンをクリックしてからの経過時間または残り時間を表示するプログラムです。経過時間を測定する他の例としては、レーシング ゲームのラップ タイムや、他のゲームのミリ秒単位の時間制限があります。

ただし、自分のストップウォッチが実際の時間と同じ速度で実行されていないため、問題が発生しています。タイマーが 1 秒上下するのに 1 秒以上かかります。

JLabelコードは次のとおりです: (GUI は完全に機能します。1 秒ごとに に表示される時間が 1 秒少なくなるように、経過時間を表示する値を制御する方法についてもっと心配しています。変更できません。Thread.sleepタイマーをさらに悪化させるため、渡される引数。)

import javax.swing.*;

import java.awt.event.*;
import java.awt.*;
import java.text.DecimalFormat;
import java.util.concurrent.*;

public class StopwatchGUI3 extends JFrame 
{

    private static final long serialVersionUID = 3545053785228009472L;

    // GUI Components
    private JPanel panel;
    private JLabel timeLabel;

    private JPanel buttonPanel;
    private JButton startButton;
    private JButton resetButton;
    private JButton stopButton;

    // Properties of Program.
    private byte centiseconds = 0;
    private byte seconds = 30;
    private short minutes = 0;

    private Runnable timeTask;
    private Runnable incrementTimeTask;
    private Runnable setTimeTask;
    private DecimalFormat timeFormatter;
    private boolean timerIsRunning = true;

    private ExecutorService executor = Executors.newCachedThreadPool();

    public StopwatchGUI3()
    {
        panel = new JPanel();
        panel.setLayout(new BorderLayout());

        timeLabel = new JLabel();
        timeLabel.setFont(new Font("Consolas", Font.PLAIN, 13));
        timeLabel.setHorizontalAlignment(JLabel.CENTER);
        panel.add(timeLabel);


        buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));

        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                if (!timerIsRunning)
                    timerIsRunning = true;

                executor.execute(timeTask);
            }
        });
        buttonPanel.add(startButton);

        resetButton = new JButton("Reset");
        resetButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                timerIsRunning = false;

                centiseconds = 0;
                seconds = 30;
                minutes = 0;

                timeLabel.setText(timeFormatter.format(minutes) + ":" 
                        + timeFormatter.format(seconds) + "." 
                        + timeFormatter.format(centiseconds));
            }
        });

        buttonPanel.add(resetButton);

        stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                timerIsRunning = false;
            }
        });

        buttonPanel.add(stopButton);


        panel.add(buttonPanel, BorderLayout.SOUTH);


        timeFormatter = new DecimalFormat("00");

        timeTask = new Runnable(){
            public void run()
            {
                while(timerIsRunning)
                {
                    executor.execute(incrementTimeTask);

                    try
                    {
                        Thread.sleep(10);
                    }
                    catch (InterruptedException ex)
                    {
                        ex.printStackTrace();
                    }
                 }
            }
        };

        incrementTimeTask = new Runnable(){
            public void run()
            {
                if (centiseconds > 0)
                    centiseconds--;
                else
                {
                    if (seconds == 0 && minutes == 0)
                        timerIsRunning = false;
                    else if (seconds > 0)
                    {
                        seconds--;
                        centiseconds = 99;
                    }
                    else if (minutes > 0)
                    {
                        minutes--;
                        seconds = 59;
                        centiseconds = 99;
                    }
                }

                executor.execute(setTimeTask);
            }
        };

        setTimeTask = new Runnable(){
            public void run()
            {
                timeLabel.setText(timeFormatter.format(minutes) + ":" 
                        + timeFormatter.format(seconds) + "." 
                        + timeFormatter.format(centiseconds));
            }
        };

        timeLabel.setText(timeFormatter.format(minutes) + ":" 
                + timeFormatter.format(seconds) + "." 
                + timeFormatter.format(centiseconds));

        add(panel);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setTitle("StopwatchGUI.java");

        pack();
        setVisible(true);
    }

    public static void main(String[] args) 
    {
        new StopwatchGUI3();
    }
}

3 つの別々のスレッドに依存する代わりに、本物のストップウォッチのように、タイマーをリアルタイムで同期させる別の方法が必要です。これは、このような大規模なプログラミング プロジェクトには多すぎると思いますが、入門レベルでは問題ありません。今。(ちなみに、DecimalFormatクラスは実際のストップウォッチのように数値を適切にフォーマットすることですが、四捨五入する小数値はありません。私がこれを投稿した時点で、 というテキストクラスが存在するのは今だけSimpleDateFormatです。)

つまり、このプログラムを単なるストップウォッチにしたいのです。そうでない場合、たとえば Java ゲームでストップウォッチを作成または使用するにはどうすればよいでしょうか。

4

2 に答える 2

4

直面する最大の問題は、さまざまなRunnableを一定の速度で実行できるようにすることです。基本的に、提供されたタスクを が実際にいつ実行するかを知る実際の方法はありませんExecutor。独自のオーバーヘッドがあるためです。

この特定のケースでは、アクティブなThreads の数を 1 つに減らすことをお勧めします。これにより、他の s の作成と実行に関連する追加のオーバーヘッドが削減され、Thread必要な時間にできるだけ近づくように最適な制御が提供されます。できるだけ。

を使用するThread代わりに、代わりに , primary を使用します。javax.swing.Timerこれはシンプルであり、EDT のコンテキスト内で実行されるため、内部から UI をより安全に更新できます。たとえば、

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class StopwatchGUI3 extends JFrame {

    private static final long serialVersionUID = 3545053785228009472L;

    // GUI Components
    private JPanel panel;
    private JLabel timeLabel;

    private JPanel buttonPanel;
    private JButton startButton;
    private JButton resetButton;
    private JButton stopButton;

    // Properties of Program.
    private byte centiseconds = 0;
    private byte seconds = 30;
    private short minutes = 0;

    private DecimalFormat timeFormatter;

    private Timer timer;

    public StopwatchGUI3() {
        panel = new JPanel();
        panel.setLayout(new BorderLayout());

        timeLabel = new JLabel();
        timeLabel.setFont(new Font("Consolas", Font.PLAIN, 13));
        timeLabel.setHorizontalAlignment(JLabel.CENTER);
        panel.add(timeLabel);

        buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));

        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                timer.start();

            }
        });
        buttonPanel.add(startButton);

        resetButton = new JButton("Reset");
        resetButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                timer.stop();

                centiseconds = 0;
                seconds = 30;
                minutes = 0;

                timeLabel.setText(timeFormatter.format(minutes) + ":"
                        + timeFormatter.format(seconds) + "."
                        + timeFormatter.format(centiseconds));
            }
        });

        buttonPanel.add(resetButton);

        stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                timer.stop();
            }
        });

        buttonPanel.add(stopButton);

        panel.add(buttonPanel, BorderLayout.SOUTH);

        timeFormatter = new DecimalFormat("00");

        timer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (centiseconds > 0) {
                    centiseconds--;
                } else {
                    if (seconds == 0 && minutes == 0) {
                        timer.stop();
                    } else if (seconds > 0) {
                        seconds--;
                        centiseconds = 99;
                    } else if (minutes > 0) {
                        minutes--;
                        seconds = 59;
                        centiseconds = 99;
                    }
                }
                timeLabel.setText(timeFormatter.format(minutes) + ":"
                        + timeFormatter.format(seconds) + "."
                        + timeFormatter.format(centiseconds));
            }
        });

        timeLabel.setText(timeFormatter.format(minutes) + ":"
                + timeFormatter.format(seconds) + "."
                + timeFormatter.format(centiseconds));

        add(panel);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setTitle("StopwatchGUI.java");

        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                new StopwatchGUI3();
            }
        });
    }
}

その時点で「推測」もやめます。「更新」の間に経過した時間が正確であるという保証はまったくありません。

代わりに、ストップウォッチが開始されたときに現在の時間を取得し、 の各ティックでTimer現在の時間から減算して、経過した時間を取得します。それを使用して、ストップウォッチの現在の値を決定できます...

例えば...

次のインスタンス フィールドを追加しています...

private long startTime;
private long runTime = 30000; // 30 seconds...

を更新しstartButtonて、開始時刻のキャプチャを含めています...

startButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {

        startTime = System.currentTimeMillis();
        timer.start();

    }
});

そして、次のように更新Timerします...

timer = new Timer(10, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {

        long now = System.currentTimeMillis();
        long dif = now - startTime;
        if (dif >= runTime) {

            timer.stop();
            dif = runTime;

        }

        dif = runTime - dif;

        long minutes = dif / (60 * 1000);
        dif = Math.round(dif % (60 * 1000));
        long seconds = dif / 1000;
        dif = Math.round(dif % 1000);
        long centiseconds = dif / 10;

        timeLabel.setText(timeFormatter.format(minutes) + ":"
                + timeFormatter.format(seconds) + "."
                + timeFormatter.format(centiseconds));
    }
});

詳細については、Swing での同時実行をご覧ください。

于 2014-01-09T05:13:31.370 に答える
2

「このような大規模なプログラミング プロジェクトには多すぎると思われる 3 つの個別のスレッドに依存する代わりに、本物のストップウォッチのように、タイマーをリアルタイムで同期させる別の方法が必要です。」

を使用できますjavax.swing.Timer。使い方はかなり簡単です。基本的なコンストラクタは次のようになります

Timer(int duration, ActionListener listener)

したがって、できることは、Timerオブジェクトをクラス メンバーとして宣言することです。次に、コンストラクターで初期化します。このようなもの

public Constructor() {
    timer = new Timer(1000, new ActionLisentener(){
        public void actionPerformed(ActionEvent e) {
            // do something
        }
    });
}

タイマーでインクリメントするカウントを持つことができます。また、タイマーにはメソッドがstop() start()あり、restart()使用できます。

すっきりとした見た目のコンストラクターが必要な場合は、次のような内部リスナー クラスをいつでも作成できます。

private class TimerListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        // do something
    }
}

次に、このようにタイマーを初期化します

timer = new Timer(1000, new TimerListener());
于 2014-01-09T05:12:24.143 に答える