为什么我的 JLabel 没有出现 [英] Why is my JLabel not showing up

查看:91
本文介绍了为什么我的 JLabel 没有出现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的一个抽象类中调用这个名为 check 的方法,但由于某种原因,我添加到 JPanel(面板)的 JLabel(问题)没有出现.为什么会出现这种情况?任何解释,我同时使用重绘和验证方法,但仍然没有显示.

解决方案

您遇到的问题是您阻塞了事件调度线程,阻止更新 UI 或处理任何新事件...

从这里开始...

 for(int i = 0; i <15; i++){//...//检查用户是否输入了任何内容//并在这里复合而(!回答){Thread.sleep(持续时间);//...}

您显然是在以程序方式思考(就像您在控制台程序中那样),但这不是 GUI 的工作方式,GUI 是事件驱动的,在某个时间点发生某些事情并且您对其做出响应.

我的建议是研究 Swing Timer,它允许你安排一个回调,在未来某个时间点,并在它被触发时执行一些动作,可以用来修改UI,因为它在 EDT 的上下文中执行.

参见 Swing 中的并发如何使用 Swing Timers 了解更多详情

我还建议您查看 CardLayout,因为它可能更容易在不同视图之间更改

有关详细信息,请参阅如何使用 CardLayout>

基础知识

我非常遵守代码到接口而不是实现"的原则;和 模型-视图-控制器.这些基本上鼓励你分离和隔离责任,所以一个部分的变化不会对另一个产生不利影响.

这也意味着您可以即插即用的实现,解耦代码并使其更加灵活.

从基础开始,您需要一些包含文本(问题)、正确答案和一些选项"的内容.(或错误答案)

公共接口问题{公共字符串 getPrompt();公共字符串 getCorrectAnswer();公共字符串[] getOptions();公共字符串 getUserResponse();public void setUserResponse(String response);公共布尔 isCorrect();}

所以,非常基本.问题有提示、正确答案、一些错误答案,并且可以管理用户响应.为了方便使用,它还有一个isCorrect方法

现在,我们需要一个实际的实现.这是一个非常基本的示例,但您可能有许多不同的实现,甚至可能包括答案类型的泛型(为了参数起见,我将其假定为 String)

public class DefaultQuestion 实现 Question {私人最终字符串提示;私人最终字符串正确答案;私人最终 String[] 选项;私人字符串用户响应;公共默认问题(字符串提示,字符串正确答案,字符串...选项){this.prompt = 提示;this.correctAnswer = 正确答案;this.options = 选项;}@覆盖公共字符串 getPrompt() {返回提示;}@覆盖公共字符串 getCorrectAnswer() {返回正确答案;}@覆盖公共字符串[] getOptions() {退货选项;}@覆盖公共字符串 getUserResponse() {返回用户响应;}@覆盖public void setUserResponse(字符串响应){用户响应 = 响应;}@覆盖公共布尔 isCorrect() {返回 getCorrectAnswer().equals(getUserResponse());}}

好的,这一切都很好,但这对我们有什么实际作用?好吧,知道您可以创建一个简单的组件,其唯一的工作就是向用户提出问题并处理他们的回应......

public class QuestionPane extends JPanel {私人问题;公共问题窗格(问题问题){this.question = 问题;setLayout(new BorderLayout());JLabel prompt = new JLabel("<html><b>" + question.getPrompt() + "</b></html>");prompt.setHorizo​​ntalAlignment(JLabel.LEFT);添加(提示,BorderLayout.NORTH);JPanel 猜测 = new JPanel(new GridBagLayout());guesses.setBorder(new EmptyBorder(5, 5, 5, 5));GridBagConstraints gbc = 新的 GridBagConstraints();gbc.gridwidth = GridBagConstraints.REMAINDER;gbc.weightx = 1;gbc.anchor = GridBagConstraints.WEST;列表<字符串>options = new ArrayList<>(Arrays.asList(question.getOptions()));options.add(question.getCorrectAnswer());Collections.sort(选项);ButtonGroup bg = new ButtonGroup();对于(字符串选项:选项){JRadioButton btn = new JRadioButton(option);bg.add(btn);猜测.add(btn, gbc);}添加(猜测);}公共问题 getQuestion() {返回问题;}公共类 ActionHandler 实现 ActionListener {@覆盖public void actionPerformed(ActionEvent e) {getQuestion().setUserResponse(e.getActionCommand());}}}

这是一个很好的可重用组件,一个可以处理一堆问题而无需关心的组件.

现在,我们需要一些方法来管理多个问题,一个测验!

public class QuizPane extends JPanel {私人列表<问题>测验;私人长时间超时 = 5;私人定时器定时器;接下来是私有 JButton;私人 CardLayout cardLayout;私人 int currentQuestion;私人 JPanel panelOfQuestions;私人长开始时间;公共测验窗格(列表<问题>测验){this.quiz = 测验;cardLayout = new CardLayout();panelOfQuestions = new JPanel(cardLayout);JButton start = new JButton("Start");start.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {当前问题 = -1;下一个问题();定时器开始();}});JPanel 填充器 = new JPanel(new GridBagLayout());填充物.添加(开始);panelOfQuestions.add(filler, 开始");for (int index = 0; index < quiz.size(); index++) {问题 question = quiz.get(index);QuestionPane 窗格 = new QuestionPane(question);panelOfQuestions.add(pane, Integer.toString(index));}panelOfQuestions.add(new JLabel(测验结束"),last");当前问题 = 0;cardLayout.show(panelOfQuestions,开始");setLayout(new BorderLayout());添加(panelOfQuestions);JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));next = new JButton("Next");buttonPane.add(next);next.setEnabled(false);添加(buttonPane,BorderLayout.SOUTH);next.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {下一个问题();}});计时器 = 新计时器(250,新动作监听器(){@覆盖public void actionPerformed(ActionEvent e) {如果(开始时间 == 空){startTime = System.currentTimeMillis();}长持续时间 = (System.currentTimeMillis() - startTime)/1000;如果(持续时间>=超时){下一个问题();} 别的 {long timeLeft = timeOut - 持续时间;next.setText("Next (" + timeLeft + ")");next.repaint();}}});}受保护的无效 nextQuestion() {定时器停止();当前问题++;if (currentQuestion >= quiz.size()) {cardLayout.show(panelOfQuestions, last");next.setEnabled(false);//您可以遍历所有问题并进行计数//正确答案在这里} 别的 {cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));开始时间 = 空;next.setText(下一步");next.setEnabled(true);定时器开始();}}}

好吧,这有点复杂,但最基本的是,它管理当前呈现给用户的问题,管理时间并允许用户导航到下一个问题(如果他们愿意).

现在,很容易迷失在细节中......

这部分代码实际上使用CardLayout

设置了主视图"

panelOfQuestions.add(filler, "start");for (int index = 0; index < quiz.size(); index++) {问题 question = quiz.get(index);QuestionPane 窗格 = new QuestionPane(question);panelOfQuestions.add(pane, Integer.toString(index));}panelOfQuestions.add(new JLabel(测验结束"),last");当前问题 = 0;cardLayout.show(panelOfQuestions,开始");

start 按钮,结束画面"并且每个单独的 QuestionPane 都被添加到由 CardLayout 管理的 panelOfQuestions 中,这使得翻转"很容易.根据需要查看视图.

我用一个简单的方法转到下一个问题

protected void nextQuestion() {定时器停止();当前问题++;if (currentQuestion >= quiz.size()) {cardLayout.show(panelOfQuestions, last");next.setEnabled(false);//您可以遍历所有问题并进行计数//正确答案在这里} 别的 {cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));开始时间 = 空;next.setText(下一步");next.setEnabled(true);定时器开始();}}

这基本上会增加一个计数器并检查我们是否已经用完了问题.如果有,它会禁用下一个按钮并显示最后一个"按钮.查看给用户,如果没有,则移动到下一个问题视图并重新启动超时计时器.

现在,魔法"来了……

timer = new Timer(250, new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {如果(开始时间 == 空){startTime = System.currentTimeMillis();}长持续时间 = (System.currentTimeMillis() - startTime)/1000;如果(持续时间>=超时){下一个问题();} 别的 {long timeLeft = timeOut - 持续时间;next.setText("Next (" + timeLeft + ")");}}});

Swing Timer 充当伪循环,这意味着它会定期调用 actionPerformed 方法,就像 forwhile 循环会,但它不会阻塞 EDT.

这个例子增加了一点魔法"因为它充当倒数计时器,它检查问题对用户可见的时间并显示倒计时,直到它会自动移动到下一个问题,当 duration 大于时或等于 timeOut(本例中为 5 秒),它调用 nextQuestion 方法

但是你问你如何使用它?您创建 QuestionList,创建 QuizPane 的实例并将其添加到屏幕上显示的其他容器中,例如例如...

公共类 QuizMaster {公共静态无效主(字符串 [] args){新的测验大师();}公共测验大师(){EventQueue.invokeLater(new Runnable() {@覆盖公共无效运行(){尝试 {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {ex.printStackTrace();}列出<问题>测验 = 新的 ArrayList(5);quiz.add(new DefaultQuestion(香蕉是:"、黄色"、绿色"、蓝色"、平"、圆形"));quiz.add(new DefaultQuestion(1 + 1:", 2", 5", 3", 人工构造"));quiz.add(new DefaultQuestion(在英国,吃东西是违法的……"、圣诞节吃肉馅饼"、你的堂兄"、香蕉"));quiz.add(new DefaultQuestion(如果你把袋鼠的尾巴抬离地面……"、它不能跳"、它会踢你的脸"、充当千斤顶";));JFrame frame = new JFrame(测试");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(new QuizPane(quiz));框架.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}});}}

最后,因为我知道你会想要一个,一个完全可运行的例子

import java.awt.BorderLayout;导入 java.awt.CardLayout;导入 java.awt.EventQueue;导入 java.awt.FlowLayout;导入 java.awt.GridBagConstraints;导入 java.awt.GridBagLayout;导入 java.awt.event.ActionEvent;导入 java.awt.event.ActionListener;导入 java.util.ArrayList;导入 java.util.Arrays;导入 java.util.Collections;导入 java.util.List;导入 javax.swing.ButtonGroup;导入 javax.swing.JButton;导入 javax.swing.JFrame;导入 javax.swing.JLabel;导入 javax.swing.JPanel;导入 javax.swing.JRadioButton;导入 javax.swing.Timer;导入 javax.swing.UIManager;导入 javax.swing.UnsupportedLookAndFeelException;导入 javax.swing.border.EmptyBorder;公开课测验大师{公共静态无效主(字符串 [] args){新的测验大师();}公共测验大师(){EventQueue.invokeLater(new Runnable() {@覆盖公共无效运行(){尝试 {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {ex.printStackTrace();}列出<问题>测验 = 新的 ArrayList(5);quiz.add(new DefaultQuestion(香蕉是:"、黄色"、绿色"、蓝色"、平"、圆形"));quiz.add(new DefaultQuestion(1 + 1:", 2", 5", 3", 人工构造"));quiz.add(new DefaultQuestion(在英国,吃东西是违法的……"、圣诞节吃肉馅饼"、你的堂兄"、香蕉"));quiz.add(new DefaultQuestion(如果你把袋鼠的尾巴抬离地面……"、它不能跳"、它会踢你的脸"、充当千斤顶";));JFrame frame = new JFrame(测试");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(new QuizPane(quiz));框架.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}});}公共类 QuizPane 扩展 JPanel {私人列表<问题>测验;私人长时间超时 = 5;私人定时器定时器;接下来是私有 JButton;私人 CardLayout cardLayout;私人 int currentQuestion;私人 JPanel panelOfQuestions;私人长开始时间;公共测验窗格(列表<问题>测验){this.quiz = 测验;cardLayout = new CardLayout();panelOfQuestions = new JPanel(cardLayout);JButton start = new JButton("Start");start.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {当前问题 = -1;下一个问题();定时器开始();}});JPanel 填充器 = new JPanel(new GridBagLayout());填充物.添加(开始);panelOfQuestions.add(filler, 开始");for (int index = 0; index < quiz.size(); index++) {问题 question = quiz.get(index);QuestionPane 窗格 = new QuestionPane(question);panelOfQuestions.add(pane, Integer.toString(index));}panelOfQuestions.add(new JLabel(测验结束"),last");当前问题 = 0;cardLayout.show(panelOfQuestions,开始");setLayout(new BorderLayout());添加(panelOfQuestions);JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));next = new JButton("Next");buttonPane.add(next);next.setEnabled(false);添加(buttonPane,BorderLayout.SOUTH);next.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {下一个问题();}});计时器 = 新计时器(250,新动作监听器(){@覆盖public void actionPerformed(ActionEvent e) {如果(开始时间 == 空){startTime = System.currentTimeMillis();}长持续时间 = (System.currentTimeMillis() - startTime)/1000;如果(持续时间>=超时){下一个问题();} 别的 {long timeLeft = timeOut - 持续时间;next.setText("Next (" + timeLeft + ")");next.repaint();}}});}受保护的无效 nextQuestion() {定时器停止();当前问题++;if (currentQuestion >= quiz.size()) {cardLayout.show(panelOfQuestions, last");next.setEnabled(false);//您可以遍历所有问题并进行计数//正确答案在这里} 别的 {cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));开始时间 = 空;next.setText(下一步");next.setEnabled(true);定时器开始();}}}公共接口问题{公共字符串 getPrompt();公共字符串 getCorrectAnswer();公共字符串[] getOptions();公共字符串 getUserResponse();public void setUserResponse(String response);公共布尔 isCorrect();}公共类 DefaultQuestion 实现问题 {私人最终字符串提示;私人最终字符串正确答案;私人最终 String[] 选项;私人字符串用户响应;公共默认问题(字符串提示,字符串正确答案,字符串...选项){this.prompt = 提示;this.correctAnswer = 正确答案;this.options = 选项;}@覆盖公共字符串 getPrompt() {返回提示;}@覆盖公共字符串 getCorrectAnswer() {返回正确答案;}@覆盖公共字符串[] getOptions() {退货选项;}@覆盖公共字符串 getUserResponse() {返回用户响应;}@覆盖public void setUserResponse(字符串响应){用户响应 = 响应;}@覆盖公共布尔 isCorrect() {返回 getCorrectAnswer().equals(getUserResponse());}}公共类 QuestionPane 扩展 JPanel {私人问题;公共问题窗格(问题问题){this.question = 问题;setLayout(new BorderLayout());JLabel prompt = new JLabel("<html><b>" + question.getPrompt() + "</b></html>");prompt.setHorizo​​ntalAlignment(JLabel.LEFT);添加(提示,BorderLayout.NORTH);JPanel 猜测 = new JPanel(new GridBagLayout());guesses.setBorder(new EmptyBorder(5, 5, 5, 5));GridBagConstraints gbc = 新的 GridBagConstraints();gbc.gridwidth = GridBagConstraints.REMAINDER;gbc.weightx = 1;gbc.anchor = GridBagConstraints.WEST;列表<字符串>options = new ArrayList<>(Arrays.asList(question.getOptions()));options.add(question.getCorrectAnswer());Collections.sort(选项);ButtonGroup bg = new ButtonGroup();对于(字符串选项:选项){JRadioButton btn = new JRadioButton(option);bg.add(btn);猜测.add(btn, gbc);}添加(猜测);}公共问题 getQuestion() {返回问题;}公共类 ActionHandler 实现 ActionListener {@覆盖public void actionPerformed(ActionEvent e) {getQuestion().setUserResponse(e.getActionCommand());}}}}

I am calling this method called check in one of my abstract classes but for some reason the JLabel (problem) I am adding to the JPanel (panel) is not showing up. Why is this occurring? Any explanations, I am using both the repaint, and validate methods but still nothing shows up.

解决方案

The problem you're having is you're blocking the Event Dispatching Thread, prevent the UI from been updated or any new events from been processed...

It starts here...

        for(int i = 0; i < 15; i++)
        {
             //...
            
            //Check to see if user has enetered anything
            // And is compounded here
            while(!answered)
            {
                Thread.sleep(duration);
                //...
            }           

You're clearly thinking in a procedural manner (like you would for a console program), but this isn't how GUIs work, GUIs are event driven, something happens at some point in time and you respond to it.

My suggestion is to investigate Swing Timer, which will allow you to schedule a call back at some, point in the future and perform some action when it is triggered, which can be used to modify the UI, as its executed within the context of the EDT.

See Concurrency in Swing and How to use Swing Timers for more details

I'd also recommend that you take a look at CardLayout, as it might make easier to change the between different views

See How to Use CardLayout for more details

Basics

I work very much to the principle of "Code to interface not implementation" and the Model-View-Controller. These basically encourage your to separate and isolate responsibility, so a change in one part won't adversely affect another.

It also means you can plug'n'play implementations, decoupling the code and making it more flexible.

Start with the basic, you need something that has some text (the question), a correct answer and some "options" (or incorrect answers)

public interface Question {

    public String getPrompt();
    public String getCorrectAnswer();
    public String[] getOptions();
    public String getUserResponse();
    public void setUserResponse(String response);
    public boolean isCorrect();

}

So, pretty basic. The question has a prompt, a right answer, some wrong answers and can manage the user response. For ease of use, it also has a isCorrect method

Now, we need an actual implementation. This is a pretty basic example, but you might have a number of different implementations and might even include generics for the type of answers (which I've assumed as String for argument sake)

public class DefaultQuestion implements Question {

    private final String prompt;
    private final String correctAnswer;
    private final String[] options;

    private String userResponse;

    public DefaultQuestion(String prompt, String correctAnswer, String... options) {
        this.prompt = prompt;
        this.correctAnswer = correctAnswer;
        this.options = options;
    }

    @Override
    public String getPrompt() {
        return prompt;
    }

    @Override
    public String getCorrectAnswer() {
        return correctAnswer;
    }

    @Override
    public String[] getOptions() {
        return options;
    }

    @Override
    public String getUserResponse() {
        return userResponse;
    }

    @Override
    public void setUserResponse(String response) {
        userResponse = response;
    }

    @Override
    public boolean isCorrect() {
        return getCorrectAnswer().equals(getUserResponse());
    }
}

Okay, that's all fine and all, but what does this actually do for us? Well, know you can create a simple component whose sole job is to present the question to the user and handle their response...

public class QuestionPane extends JPanel {

    private Question question;

    public QuestionPane(Question question) {
        this.question = question;

        setLayout(new BorderLayout());

        JLabel prompt = new JLabel("<html><b>" + question.getPrompt() + "</b></html>");
        prompt.setHorizontalAlignment(JLabel.LEFT);

        add(prompt, BorderLayout.NORTH);

        JPanel guesses = new JPanel(new GridBagLayout());
        guesses.setBorder(new EmptyBorder(5, 5, 5, 5));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.weightx = 1;
        gbc.anchor = GridBagConstraints.WEST;

        List<String> options = new ArrayList<>(Arrays.asList(question.getOptions()));
        options.add(question.getCorrectAnswer());
        Collections.sort(options);

        ButtonGroup bg = new ButtonGroup();
        for (String option : options) {
            JRadioButton btn = new JRadioButton(option);
            bg.add(btn);

            guesses.add(btn, gbc);
        }

        add(guesses);

    }

    public Question getQuestion() {
        return question;
    }

    public class ActionHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            getQuestion().setUserResponse(e.getActionCommand());
        }

    }

}

This makes for a nice re-usable component, one which can handle a bunch of questions and not care.

Now, we need some way to manage multiple questions, a quiz!

public class QuizPane extends JPanel {

    private List<Question> quiz;

    private long timeOut = 5;
    private Timer timer;
    private JButton next;

    private CardLayout cardLayout;
    private int currentQuestion;

    private JPanel panelOfQuestions;

    private Long startTime;

    public QuizPane(List<Question> quiz) {
        this.quiz = quiz;
        cardLayout = new CardLayout();
        panelOfQuestions = new JPanel(cardLayout);

        JButton start = new JButton("Start");
        start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                currentQuestion = -1;
                nextQuestion();
                timer.start();
            }
        });
        
        JPanel filler = new JPanel(new GridBagLayout());
        filler.add(start);
        panelOfQuestions.add(filler, "start");

        for (int index = 0; index < quiz.size(); index++) {
            Question question = quiz.get(index);
            QuestionPane pane = new QuestionPane(question);
            panelOfQuestions.add(pane, Integer.toString(index));
        }
        panelOfQuestions.add(new JLabel("The quiz is over"), "last");
        currentQuestion = 0;
        cardLayout.show(panelOfQuestions, "start");

        setLayout(new BorderLayout());
        add(panelOfQuestions);

        JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        next = new JButton("Next");
        buttonPane.add(next);
        next.setEnabled(false);

        add(buttonPane, BorderLayout.SOUTH);

        next.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                nextQuestion();
            }
        });

        timer = new Timer(250, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (startTime == null) {
                    startTime = System.currentTimeMillis();
                }
                long duration = (System.currentTimeMillis() - startTime) / 1000;
                if (duration >= timeOut) {
                    nextQuestion();
                } else {
                    long timeLeft = timeOut - duration;
                    next.setText("Next (" + timeLeft + ")");
                    next.repaint();
                }
            }
        });
    }

    protected void nextQuestion() {
        timer.stop();
        currentQuestion++;
        if (currentQuestion >= quiz.size()) {
            cardLayout.show(panelOfQuestions, "last");
            next.setEnabled(false);
            // You could could loop through all the questions and tally
            // the correct answers here
        } else {
            cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));
            startTime = null;
            next.setText("Next");
            next.setEnabled(true);
            timer.start();
        }
    }
}

Okay, this is a little more complicated, but the basics are, it manages which question is currently presented to the user, manages the time and allows the user to navigate to the next question if they want to.

Now, it's easy to get lost in the detail...

This part of the code actually set's up the main "view", using a CardLayout

panelOfQuestions.add(filler, "start");

for (int index = 0; index < quiz.size(); index++) {
    Question question = quiz.get(index);
    QuestionPane pane = new QuestionPane(question);
    panelOfQuestions.add(pane, Integer.toString(index));
}
panelOfQuestions.add(new JLabel("The quiz is over"), "last");
currentQuestion = 0;
cardLayout.show(panelOfQuestions, "start");

The start button, "end screen" and each individual QuestionPane are added to the panelOfQuestions, which is managed by a CardLayout, this makes it easy to "flip" the views as required.

I use a simple method to move to the next question

protected void nextQuestion() {
    timer.stop();
    currentQuestion++;
    if (currentQuestion >= quiz.size()) {
        cardLayout.show(panelOfQuestions, "last");
        next.setEnabled(false);
        // You could could loop through all the questions and tally
        // the correct answers here
    } else {
        cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));
        startTime = null;
        next.setText("Next");
        next.setEnabled(true);
        timer.start();
    }
}

This basically increments a counter and checks to see if we've run out of questions or not. If we have, it disables the next button and shows the "last" view to the user, if not, it moves to the next question view and restarts the timeout timer.

Now, here comes the "magic"...

timer = new Timer(250, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (startTime == null) {
            startTime = System.currentTimeMillis();
        }
        long duration = (System.currentTimeMillis() - startTime) / 1000;
        if (duration >= timeOut) {
            nextQuestion();
        } else {
            long timeLeft = timeOut - duration;
            next.setText("Next (" + timeLeft + ")");
        }
    }
});

The Swing Timer acts a pseudo loop, meaning that it will call the actionPerformed method on a regular bases, just like for or while loop would, but it does it in such away that it doesn't block the EDT.

This example adds a little more "magic" in that it acts as a count down timer, it checks how long the question has been visible to the user and presents a count down until it will automatically move to the next question, when the duration is greater then or equal to the timeOut (5 seconds in this example), it calls the nextQuestion method

But how do you use it you ask? You create a List of Questions, create an instance of the QuizPane and add that to some other container which is displayed on the screen, for example...

public class QuizMaster {

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

    public QuizMaster() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                List<Question> quiz = new ArrayList<>(5);
                quiz.add(new DefaultQuestion("Bananas are:", "Yellow", "Green", "Blue", "Ping", "Round"));
                quiz.add(new DefaultQuestion("1 + 1:", "2", "5", "3", "An artificial construct"));
                quiz.add(new DefaultQuestion("In the UK, it is illegal to eat...", "Mince pies on Christmas Day", "Your cousin", "Bananas"));
                quiz.add(new DefaultQuestion("If you lift a kangaroo’s tail off the ground...", "It can’t hop", "It will kick you in the face", "Act as a jack"));

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new QuizPane(quiz));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

And finally, because I know you'll want one, a fully runable example

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class QuizMaster {

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

    public QuizMaster() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                List<Question> quiz = new ArrayList<>(5);
                quiz.add(new DefaultQuestion("Bananas are:", "Yellow", "Green", "Blue", "Ping", "Round"));
                quiz.add(new DefaultQuestion("1 + 1:", "2", "5", "3", "An artificial construct"));
                quiz.add(new DefaultQuestion("In the UK, it is illegal to eat...", "Mince pies on Christmas Day", "Your cousin", "Bananas"));
                quiz.add(new DefaultQuestion("If you lift a kangaroo’s tail off the ground...", "It can’t hop", "It will kick you in the face", "Act as a jack"));

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new QuizPane(quiz));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class QuizPane extends JPanel {

        private List<Question> quiz;

        private long timeOut = 5;
        private Timer timer;
        private JButton next;

        private CardLayout cardLayout;
        private int currentQuestion;

        private JPanel panelOfQuestions;

        private Long startTime;

        public QuizPane(List<Question> quiz) {
            this.quiz = quiz;
            cardLayout = new CardLayout();
            panelOfQuestions = new JPanel(cardLayout);

            JButton start = new JButton("Start");
            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    currentQuestion = -1;
                    nextQuestion();
                    timer.start();
                }
            });
            
            JPanel filler = new JPanel(new GridBagLayout());
            filler.add(start);
            panelOfQuestions.add(filler, "start");

            for (int index = 0; index < quiz.size(); index++) {
                Question question = quiz.get(index);
                QuestionPane pane = new QuestionPane(question);
                panelOfQuestions.add(pane, Integer.toString(index));
            }
            panelOfQuestions.add(new JLabel("The quiz is over"), "last");
            currentQuestion = 0;
            cardLayout.show(panelOfQuestions, "start");

            setLayout(new BorderLayout());
            add(panelOfQuestions);

            JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            next = new JButton("Next");
            buttonPane.add(next);
            next.setEnabled(false);

            add(buttonPane, BorderLayout.SOUTH);

            next.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    nextQuestion();
                }
            });

            timer = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (startTime == null) {
                        startTime = System.currentTimeMillis();
                    }
                    long duration = (System.currentTimeMillis() - startTime) / 1000;
                    if (duration >= timeOut) {
                        nextQuestion();
                    } else {
                        long timeLeft = timeOut - duration;
                        next.setText("Next (" + timeLeft + ")");
                        next.repaint();
                    }
                }
            });
        }

        protected void nextQuestion() {
            timer.stop();
            currentQuestion++;
            if (currentQuestion >= quiz.size()) {
                cardLayout.show(panelOfQuestions, "last");
                next.setEnabled(false);
                // You could could loop through all the questions and tally
                // the correct answers here
            } else {
                cardLayout.show(panelOfQuestions, Integer.toString(currentQuestion));
                startTime = null;
                next.setText("Next");
                next.setEnabled(true);
                timer.start();
            }
        }
    }

    public interface Question {

        public String getPrompt();

        public String getCorrectAnswer();

        public String[] getOptions();

        public String getUserResponse();

        public void setUserResponse(String response);

        public boolean isCorrect();
    }

    public class DefaultQuestion implements Question {

        private final String prompt;
        private final String correctAnswer;
        private final String[] options;

        private String userResponse;

        public DefaultQuestion(String prompt, String correctAnswer, String... options) {
            this.prompt = prompt;
            this.correctAnswer = correctAnswer;
            this.options = options;
        }

        @Override
        public String getPrompt() {
            return prompt;
        }

        @Override
        public String getCorrectAnswer() {
            return correctAnswer;
        }

        @Override
        public String[] getOptions() {
            return options;
        }

        @Override
        public String getUserResponse() {
            return userResponse;
        }

        @Override
        public void setUserResponse(String response) {
            userResponse = response;
        }

        @Override
        public boolean isCorrect() {
            return getCorrectAnswer().equals(getUserResponse());
        }
    }

    public class QuestionPane extends JPanel {

        private Question question;

        public QuestionPane(Question question) {
            this.question = question;

            setLayout(new BorderLayout());

            JLabel prompt = new JLabel("<html><b>" + question.getPrompt() + "</b></html>");
            prompt.setHorizontalAlignment(JLabel.LEFT);

            add(prompt, BorderLayout.NORTH);

            JPanel guesses = new JPanel(new GridBagLayout());
            guesses.setBorder(new EmptyBorder(5, 5, 5, 5));
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.weightx = 1;
            gbc.anchor = GridBagConstraints.WEST;

            List<String> options = new ArrayList<>(Arrays.asList(question.getOptions()));
            options.add(question.getCorrectAnswer());
            Collections.sort(options);

            ButtonGroup bg = new ButtonGroup();
            for (String option : options) {
                JRadioButton btn = new JRadioButton(option);
                bg.add(btn);

                guesses.add(btn, gbc);
            }

            add(guesses);

        }

        public Question getQuestion() {
            return question;
        }

        public class ActionHandler implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                getQuestion().setUserResponse(e.getActionCommand());
            }

        }

    }

}

这篇关于为什么我的 JLabel 没有出现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆