Как сделать обратный отсчет таймера вместе с индикатором выполнения?

#java #swing #timer #long-integer #jprogressbar

#java — язык #качать #таймер #длинное целое число #jprogressbar #java

Вопрос:

Как я могу сделать так, чтобы индикатор выполнения медленно снижался с течением времени?

 class GamePanel extends JPanel implements MouseListener, ActionListener
{
    private JButton quit;
    private JButton q;
    private Font loadFont;

    public GamePanel()
    {
        setBackground(Color.blue); // sets background color
        this.setLayout(null);
        quit = new JButton("Quit");
        quit.addActionListener(this);
        quit.setBounds(550, 700, 100, 30);
        this.add(quit);

        q = new JButton("Questions");
        q.addActionListener(this);
        q.setBounds(100, 100, 120, 30);
        this.add(q);

        loadFont = new Font("Serif", Font.PLAIN, 30);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.black);
        g.fillRect(80, 100, 610, 560);
        g.setColor(Color.white);
        g.fillRect(90, 110, 110, 100);// 1st column
        g.fillRect(90, 220, 110, 100);//
        g.fillRect(90, 330, 110, 100);//
        g.fillRect(90, 440, 110, 100);//
        g.fillRect(90, 550, 110, 100);//
        g.fillRect(210, 110, 110, 100);// 2nd column
        g.fillRect(210, 220, 110, 100);//
        g.fillRect(210, 330, 110, 100);//
        g.fillRect(210, 440, 110, 100);//
        g.fillRect(210, 550, 110, 100);//
        g.fillRect(330, 110, 110, 100);// 3rd column
        g.fillRect(330, 220, 110, 100);//
        g.fillRect(330, 330, 110, 100);//
        g.fillRect(330, 440, 110, 100);//
        g.fillRect(330, 550, 110, 100);//
        g.fillRect(450, 110, 110, 100);// 4th column
        g.fillRect(450, 220, 110, 100);//
        g.fillRect(450, 330, 110, 100);//
        g.fillRect(450, 440, 110, 100);//
        g.fillRect(450, 550, 110, 100);//
        g.fillRect(570, 110, 110, 100);// 5th column
        g.fillRect(570, 220, 110, 100);//
        g.fillRect(570, 330, 110, 100);//
        g.fillRect(570, 440, 110, 100);//
        g.fillRect(570, 550, 110, 100);//
        g.setColor(Color.green);
        g.setFont(loadFont);
        g.drawString(input   ":", 100, 710);
    }

    public void actionPerformed(ActionEvent e)
    {
        String order = e.getActionCommand();
        if(order.equals("Quit"))
            cards.show(c, "Introduction");
        if(order.equals("Questions"))
            cards.show(c, "Questions");
    }

    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
}

class QuestionPanel extends JPanel implements ActionListener
{
    private long startTime, elapsedTime;
    private Timer timer;
    private int countdown;
    private Font loadFont;

    public QuestionPanel()
    {
        setBackground(Color.pink); // sets background color
        this.setLayout(null);  // moved into constructor from ActionPerformed: only change layout in constructor
        startTime = 0;
        elapsedTime = 0;
        countdown = 590;
        loadFont = new Font("Segoe Script", Font.BOLD, 20);
        if(timer == null)
        {// use the biggest value possible that provides your desired time keeping precision (usually no less than 15 on Windows)
            timer = new Timer(100, this);  
            startTime = System.currentTimeMillis();  // gets start time in milliseconds
            timer.start();
        }
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.fillRect(100, 100, 600, 25);
        g.setColor(Color.green);
        g.fillRect(105, 105, countdown, 15);
        g.setColor(Color.black);
        g.setFont(loadFont);
        g.drawString(""   ((System.currentTimeMillis() - startTime) / 1000.0), 100, 80);  // display remaining time
    }

    public void actionPerformed(ActionEvent e)
    {
        String command = e.getActionCommand();
        elapsedTime = System.currentTimeMillis() - startTime;
        if(elapsedTime < (5000))
        {
            countdown--;
            repaint();
        }
        else
        {
            timer.stop();
            if(timer == null)
            {
                timer = new Timer(500, this);
                timer.start();
            }
        }
        if(elapsedTime >= (5000))  // can't use == here because of limited precision of system clock
            cards.show(c, "Correct!");
    }
}

class AnswerPanel extends JPanel implements ActionListener
{
    private JButton revert;

    public AnswerPanel()
    {
        setBackground(Color.yellow); // sets background color
        this.setLayout(null);
        revert = new JButton("Back");
        revert.addActionListener(this);
        revert.setBounds(340, 700, 100, 30);
        this.add(revert);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
    }

    public void actionPerformed(ActionEvent e)
    {
        String directive = e.getActionCommand();
        if(directive.equals("Back"))
            cards.show(c, "Start");
    }
}

class FailPanel extends JPanel implements ActionListener
{
    private JButton turnaround;

    public FailPanel()
    {
        setBackground(Color.green); // sets background color
        this.setLayout(null);
        turnaround = new JButton("Back");
        turnaround.addActionListener(this);
        turnaround.setBounds(340, 700, 100, 30);
        this.add(turnaround);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
    }

    public void actionPerformed(ActionEvent e)
    {
        String bidding = e.getActionCommand();
        if(bidding.equals("Back"))
            cards.show(c, "Start");
    }
}
}// end of the entire program
  

Комментарии:

1. Я бы хотел, чтобы люди взглянули на панель вопросов. Я пытаюсь запустить обратный отсчет таймера, пока индикатор выполнения также снижается. Когда таймер достигает 0 (после запуска с 5), предполагается, что будет показана другая панель. Другая проблема заключается в том, что таймер запускается еще до того, как я доберусь до этой панели; Я хочу иметь возможность контролировать, когда запускается таймер.

2. Святая стена текста, Бэтмен! Я бы попробовал свести проблему к настолько небольшому примеру, насколько вы можете это сделать.

3. Я решительно поддерживаю предложение СКРМ. Это просто слишком много кода. Вам придется приложить некоторые усилия, чтобы сделать его одновременно меньшим и понятным, но это потраченные не зря усилия. В противном случае у многих найдутся дела поважнее, чем проводить воскресные вечера за чтением трехтомника по программному обеспечению.

4. @thynoob: У меня есть 5 писем для тебя SSCCE . Подготовьте SSCCE, и, скорее всего, проблема (а также решение) станут понятны по мере того, как вы это сделаете. Если этого не произойдет, по крайней мере, у вас будет короткий фрагмент кода, который другие люди, вероятно, рассмотрят более внимательно. Как бы то ни было, вы просите нас просмотреть почти 450 строк кода в поисках ошибки. Ваш запрос, скорее всего, будет проигнорирован. @HFOE спасибо за форматирование кода.

5. Ваша программа также выиграет от того, что вы сделаете это, поскольку это может помочь вам в достижении вашей цели отделить программную логику от кода GUI. Кроме того, вам захочется избавиться от всего этого абсолютного позиционирования и определения размеров и вместо этого использовать менеджеры компоновки, особенно если вы хотите, чтобы пользователи с мониторами разного размера и разрешением экрана могли видеть ваш графический интерфейс. Я не могу видеть это полностью на моем, например.

Ответ №1:

Извините, я все еще не мог найти мотивации на самом деле прочитать ваш код, но просто собрал этот пример на основе вопроса. Посмотрим, подскажет ли это вам какие-нибудь идеи.

Обратите внимание, что это SSCCE и использует всего 40 строк кода.

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

class CountDownProgressBar {

    Timer timer;
    JProgressBar progressBar;

    CountDownProgressBar() {
        progressBar = new JProgressBar(JProgressBar.VERTICAL, 0, 10);
        progressBar.setValue(10);
        ActionListener listener = new ActionListener() {
            int counter = 10;
            public void actionPerformed(ActionEvent ae) {
                counter--;
                progressBar.setValue(counter);
                if (counter<1) {
                    JOptionPane.showMessageDialog(null, "Kaboom!");
                    timer.stop();
                } 
            }
        };
        timer = new Timer(1000, listener);
        timer.start();
        JOptionPane.showMessageDialog(null, progressBar);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater( new Runnable() {
            public void run() {
                CountDownProgressBar cdpb = new CountDownProgressBar();
            }
        });
    }
}
  

Комментарии:

1. @thynoob: Код Эндрюса не просто короткий, он также компилируемый, исполняемый и тестируемый. Поскольку это кратко, мы можем быстро разобраться. Поскольку он компилируется и выполняется, мы все можем запускать этот код без изменений, изменять его, улучшать и исправлять любые ошибки в нем за короткий промежуток времени.

Ответ №2:

Судя по всему, весь этот код находится в большом файле Java? Это плохая идея.

У вас должна быть веская причина для определения класса как внутреннего класса, и, судя по всему, у вас ее нет для QuestionPanel и других.

Что касается проблемы, ваш paintComponent метод вызывается каждый раз, когда обновляется ваш счетчик, что сейчас происходит примерно раз в 0,1 секунды, но вы отмечаете только на 1 пиксель при каждом обновлении, так что к концу 5 секунд вы отсекаете 10 * 5 пикселей (50). Что вам следует сделать, так это обновить индикатор выполнения с помощью другого механизма, такого как вычисление текущего времени обработки:

 long processed = System.currentTimeMillis() - startTime;
double percent = Math.max(0, 1 - processed / 5000.0);
int width = (int)(590 * percent);
  

Комментарии:

1. Почему бы просто не использовать JProgressBar?

2. Я предполагаю, что это для задания, но в остальном я согласен с вами.

3. я ценю ваш выбор входных данных, но есть ли способ сделать так, чтобы время восстановления отображалось с 5 секунд?

4. Да, измените последнюю строку, в которой отображается оставшееся время. double remaining = 5 * percent; g.drawString(String.format("%.2f", remaining), 100, 80);

5. итак, я заменяю последнюю строку, которую вы только что дали, на последнюю строку кода, который вы дали мне первым? и затем, после этого, я поместил это в свой код, используя свои переменные, и все должно быть в порядке? o__o

Ответ №3:

Это определенно слишком много информации и очень широкий вопрос. Я бы сказал, что в лучшем случае вам нужно только включить код для класса, в котором находится таймер, и класса, в котором отображается индикатор выполнения.

Просматривая код, я предполагаю, что вы используете прямоугольник для рисования индикатора выполнения. Исходя из этого, одним из способов, которым вы могли бы это сделать, было бы использование переменной для хранения ширины полосы, и каждый раз, когда таймер тикает, уменьшайте ширину полосы на заданную величину. Затем просто установите ширину нарисованного прямоугольника на значение, сохраненное в переменной.