CardLayout: как заставить событие запускаться только при отображении определенной карты?

Я использую CardLayout для отображения серии вопросов. Для каждого вопроса на ярлыке отображается таймер. Я передаю вопросы и лимит времени на каждый вопрос из основного класса.

//Reference - SynforgeTutorials
public class Quiz extends JFrame{
    JPanel p=new JPanel();
    CardLayout cards=new CardLayout();
    int numQs;


    int cardnumber;

    CL1 questions[];



    public static void main(String[] args) {

        String[] messages = {"What is area of a circle?","When does spring start?","When is the next leap year?","How many days in a week?","What causes seasons?"};
        int[] mins = {1,2,0,1,1};
        int[] secs = {30,0,30,0,0};
        new Quiz(messages,mins,secs);
    } 

    public Quiz(String messages[],int mins[],int secs[]){


        questions = new CL1[messages.length];

        for(int i=0;i<questions.length;i++)
        {
        questions[i] = new CL1(messages[i],mins[i],secs[i],this);

        }

        p.setLayout(cards);

        numQs=questions.length;
        for(int i=0;i<numQs;i++){
            p.add(questions[i],"q"+i);
        }

        cardnumber = 0;

        cards.show(p,"q"+ cardnumber);


        add(p);
        setVisible(true);


    }

    public void OK(){
        if(cardnumber==(numQs-1)){

            this.dispose();

        }
        else{

            cardnumber = cardnumber + 1;
            cards.show(p,"q"+ cardnumber);
        }
    }

}

Это класс, который создает карты. Он имеет 3 панели: верхнюю панель, на которой отображается вопрос, центральную панель, на которой отображаются минуты и секунды на этикетке вместе с кнопкой «Пауза» и «Возобновить», и нижнюю панель, на которой присутствует кнопка «ОК». При нажатии кнопки «ОК» отображается следующая карточка.

public class CL1 extends JPanel implements ActionListener {

    int correctAns;

    Quiz quiz;
    int selected;
    boolean used;

    private Timer myTimer1;
    public long initialTimeInSecs;
    public long elapsedTime;
    public long convertToSecs;
    public String minutes;
    public String seconds;
    public String clockTimeString; 
    public static final int ONE_SEC = 1000;
    Font myClockFont = new Font("Serif", Font.PLAIN, 20);




    //Message
    JPanel qPanel=new JPanel();

    //Timer
    JPanel tPanel=new JPanel();
    JLabel timeLbl = new JLabel("New label");
    JButton btnPause=new JButton("Pause");
    JButton btnResume=new JButton("Resume");


    //bottom
    JPanel botPanel=new JPanel();
    JButton OK=new JButton("OK");



    public CL1(String q, int userMinutes, int userSeconds, Quiz quiz){

        this.quiz=quiz;

        setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));


        //Message
        qPanel.add(new JLabel(q));
        add(qPanel);


        //Timer
        convertToSecs = (userMinutes * 60) + userSeconds;

        initialTimeInSecs = convertToSecs;
        elapsedTime = initialTimeInSecs;

        seconds = Integer.toString((int)(elapsedTime % 60));
        minutes = Integer.toString((int)((elapsedTime % 3600) / 60));
        if (seconds.length() < 2)
            seconds = "0" + seconds;
        if (minutes.length() < 2)
            minutes = "0" + minutes;
        clockTimeString = (minutes+":"+seconds).toString();

        timeLbl.setText(clockTimeString);
        timeLbl.setFont(myClockFont);
        timeLbl.setBorder(raisedbevel);

        myTimer1 = new Timer(ONE_SEC,new ActionListener() {
            public void actionPerformed(ActionEvent evt) {

                initialTimeInSecs = initialTimeInSecs - 1;

                elapsedTime = initialTimeInSecs;

                String seconds = Integer.toString((int)(elapsedTime % 60));
                String minutes = Integer.toString((int)((elapsedTime % 3600) / 60));


                if (seconds.length() < 2)
                    seconds = "0" + seconds;

                if (minutes.length() < 2)
                    minutes = "0" + minutes;

                clockTimeString = (minutes+":"+seconds).toString();

                timeLbl.setText(clockTimeString);

                if(clockTimeString.equals("00:00"))
                {
                    myTimer1.stop();

                }
                } 

          });

        myTimer1.start(); 



        btnPause.addActionListener(new java.awt.event.ActionListener() 
        {
            public void actionPerformed(java.awt.event.ActionEvent evt) 
            {   
                PauseBtnActionPerformed(evt);
            }
        });



        btnResume.addActionListener(new java.awt.event.ActionListener() 
        {
            public void actionPerformed(java.awt.event.ActionEvent evt) 
            {   
                ResumeBtnActionPerformed(evt);
            }
        });

        tPanel.add(timeLbl);
        tPanel.add(btnPause);
        tPanel.add(btnResume);

        add(tPanel);


        //bottom

        OK.addActionListener(this);

        botPanel.add(OK);

        add(botPanel);
    }

    public void actionPerformed(ActionEvent e){
        Object src=e.getSource();
        //OK button
        if(src.equals(OK)){

            quiz.OK();}

    }

    public void PauseBtnActionPerformed(java.awt.event.ActionEvent evt)
    {
        myTimer1.stop();
    }

    public void ResumeBtnActionPerformed(java.awt.event.ActionEvent evt)
    {
        myTimer1.start();
    }
}

Таймер не обновляется с каждой новой картой. Он запускается параллельно для каждой карты, так что, когда я перехожу от одной карты к другой, новая карта показывает время, уже прошедшее на первой карте.

Например, для вопроса 1 ограничение по времени составляет 1.30. Для вопроса 2 ограничение по времени составляет 2.00. Для вопроса 3 ограничение по времени составляет 3.00.

Диалоговое окно сначала показывает первую метку и начинает уменьшаться с 1.30. Скажем, я оставался на этой метке в течение 10 секунд, прежде чем нажать «ОК».

Затем отображается вторая метка, и таймер начинает отсчет с 1,50 (2,00-0,10 вместо 2,00). Скажем, я остаюсь на этом вопросе в течение 25 секунд, прежде чем нажать «ОК».

Затем отображается третья метка, и таймер начинает отсчет с 2,25 (3,00-(0,10+0,25) вместо 3,00).

Я хочу сделать 2 вещи:

  1. Я хочу, чтобы таймер для каждой карты запускался только тогда, когда эта карта отображается.
  2. Я хочу, чтобы карта автоматически менялась на следующую карту, когда лимит времени достигает 00:00.

Любые предложения будут полезны. Заранее спасибо!


person sanaku    schedule 06.04.2015    source источник


Ответы (1)


Я хочу, чтобы таймер для каждой карты запускался только тогда, когда эта карта отображается.

Вы можете использовать HierarchyListener для прослушивания изменений в видимости каждой карты:

@Override
public void hierarchyChanged(HierarchyEvent e)
{
    JComponent component = (JComponent)e.getSource();

    if ((HierarchyEvent.SHOWING_CHANGED & e.getChangeFlags()) != 0
    &&  component.isShowing())
    {
        // start the Timer
    }
}

Таким образом, вам нужно будет добавить HierarchyListener к каждой панели, которую вы добавляете к CardLayout.

Я хочу, чтобы карта автоматически менялась на следующую карту

CardLayout поддерживают функцию «следующая карта», поэтому вы просто вызываете соответствующий метод, когда таймер достигает 0.

person camickr    schedule 06.04.2015
comment
Спасибо большое за вашу помощь. Я смог запустить таймеры индивидуально для каждой карты. Что касается второй части, как я могу перейти на следующую карту, когда таймер достигает 0? Карты меняются из класса контейнера, где как таймер присутствует в одной из панелей в вызываемом классе. - person sanaku; 10.04.2015
comment
@sanaku, прочтите CardLayout API. Он поддерживает функциональность следующей карты, поэтому вы просто вызываете метод next(...). - person camickr; 11.04.2015