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

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

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

person wannabeprogrammer    schedule 09.05.2011    source источник
comment
Я хотел бы, чтобы люди взглянули на QuestionPanel. Я пытаюсь сделать обратный отсчет таймера, в то время как индикатор выполнения также идет вниз. Когда таймер достигает 0 (после запуска с 5), предполагается, что будет показана другая панель. Другая проблема заключается в том, что таймер запускается еще до того, как я доберусь до этой панели; Я хочу иметь возможность контролировать запуск таймера.   -  person wannabeprogrammer    schedule 09.05.2011
comment
Святая стена текста, Бэтмен! Я бы попытался свести проблему к как можно меньшему примеру.   -  person corsiKa    schedule 09.05.2011
comment
я попытался сжать это, но если я сконденсирую это... людям будет труднее понять, о чем я говорю XD   -  person wannabeprogrammer    schedule 09.05.2011
comment
Я решительно поддерживаю предложение TFRM. Это просто слишком много кода. Вам придется приложить некоторые усилия, чтобы сделать его меньше и понятнее, но эти усилия потрачены не зря. В противном случае у многих будут более интересные занятия воскресными вечерами, чем чтение трехтомной книги по коду.   -  person Hovercraft Full Of Eels    schedule 09.05.2011
comment
@thynoob: у меня для вас 5 писем SSCCE. Подготовьте SSCCE, и, скорее всего, проблема (а также решение) станут ясны, когда вы это сделаете. Если этого не произойдет, по крайней мере, у вас будет короткий фрагмент кода, который другие люди, скорее всего, рассмотрят более внимательно. А так вы просите нас просмотреть почти 450 строк кода в поисках ошибки. Ваша просьба, скорее всего, будет проигнорирована. @HFOE спасибо за форматирование кода.   -  person Andrew Thompson    schedule 09.05.2011
comment
Ваша программа также выиграет от того, что вы сделаете это, поскольку это может помочь вам в вашей цели отделить логику программы от кода графического интерфейса. Кроме того, вы захотите избавиться от всего этого абсолютного позиционирования и размеров и вместо этого использовать менеджеры компоновки, особенно если вы хотите, чтобы люди с мониторами разного размера и разрешениями экрана могли видеть ваш графический интерфейс. Я, например, не вижу его полностью на моем.   -  person Hovercraft Full Of Eels    schedule 09.05.2011
comment
я сократил код... надеюсь, что это поможет: D   -  person wannabeprogrammer    schedule 09.05.2011


Ответы (3)


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

Обратите внимание, что это 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();
            }
        });
    }
}
person Andrew Thompson    schedule 09.05.2011
comment
@thynoob: код Эндрюса не просто короток, он также компилируется, запускается и тестируется. Так как это кратко, мы можем понять это быстро. Поскольку он компилируется и запускается, мы все можем запускать этот код без изменений, изменять его, улучшать и исправлять любые ошибки в нем за короткий промежуток времени. - person Hovercraft Full Of Eels; 09.05.2011

Судя по всему, весь этот код находится в большом файле 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);
person pickypg    schedule 09.05.2011
comment
Почему бы просто не использовать JProgressBar? - person Hovercraft Full Of Eels; 09.05.2011
comment
Я предполагаю, что это для задания, но в остальном я согласен с вами. - person pickypg; 09.05.2011
comment
Я ценю ваш вклад в pickypg, но есть ли способ заставить отображаемое оставшееся время начинаться с 5 секунд? - person wannabeprogrammer; 09.05.2011
comment
Да, измените последнюю строку, которая рисует оставшуюся строку времени. double remaining = 5 * percent; g.drawString(String.format("%.2f", remaining), 100, 80); - person pickypg; 09.05.2011
comment
поэтому я заменяю последнюю строку, которую ты только что дал, последней строкой кода, который ты мне дал? а затем после этого я поместил это в свой код, используя свои переменные, и все должно быть в порядке? о__о - person wannabeprogrammer; 09.05.2011
comment
@Hovercraft Full Of Eels ... pickypg правильно, это задание, но не мог бы один из вас показать мне, как работает JProgressBar? - person wannabeprogrammer; 09.05.2011
comment
@thynoob Вам придется немного поработать для себя. Но для получения справки по JProgressBar используйте руководство. - person pickypg; 09.05.2011

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

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

person Ryan    schedule 09.05.2011