为什么我的JLabel没有显示出来

我调用这个方法称为检查我的抽象类之一,但由于某种原因,我添加到JPanel(面板)的JLabel(问题)没有显示出来。 为什么会发生? 任何解释,我正在使用重绘和validation方法,但仍然没有显示出来。

你遇到的问题是你阻塞事件调度线程,防止用户界面被更新或任何新的事件被处理…

它从这里开始

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

你很清楚地以一种程序化的方式思考(就像你将要做一个控制台程序一样),但这不是GUI的工作方式,GUI是事件驱动的,某些事情发生在某个时间点,而是你回应它。

我的build议是调查摆动Timer ,这将允许您安排在某些点的callback,并在触发时执行一些操作,这可以用来修改UI,因为它在上下文中执行美东时间。

请参阅Swing中的并发和如何使用Swing定时器以获取更多详细信息

我还build议你看看CardLayout ,因为它可能会更容易地改变不同的意见之间

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

基本

我非常重视“接口不实现的代码”和“ 模型 – 视图 – 控制器 ”的原理。 这些基本上鼓励你分开和隔离责任,所以一个部分的改变不会对另一个部分产生不利影响。

这也意味着你可以插件的实现,解耦代码并使其更加灵活。

从基本开始,你需要有一些文本(问题),一个正确的答案和一些“选项”(或不正确的答案)

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

所以,很基本。 这个问题有一个提示,一个正确的答案,一些错误的答案,并可以pipe理用户的回应。 为了便于使用,它也有一个isCorrect方法

现在,我们需要一个实际的实施。 这是一个非常基本的例子,但是你可能有很多不同的实现,甚至可能包含generics的答案types(我认为它是String作为参数)

 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()); } } 

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

 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()); } } } 

这使得一个很好的可重用组件,一个可以处理一堆问题,而不关心。

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

 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(); } } } 

好吧,这有点复杂,但基本的是,它pipe理着当前呈现给用户的哪个问题,pipe理时间,并允许用户如果他们想要导航到下一个问题。

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

这部分代码实际上是使用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"); 

startbutton,“结束屏幕”和每个单独的QuestionPane被添加到由CardLayoutpipe理的panelOfQuestions ,这使得可以根据需要轻松“翻转”视图。

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

 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(); } } 

这基本上增加了一个柜台,并检查,看看我们是否已经用完了问题。 如果有,则禁用下一个button并向用户显示“最后一个”视图,如果不是,则移至下一个问题视图并重新启动超时定时器。

现在,这里是“魔法”…

 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 + ")"); } } }); 

Swing Timer起到一个伪循环的作用,意味着它将在常规的基础上调用actionPerformed方法,就像forwhile循环一样,但是这样做不会阻塞EDT。

这个例子增加了一点“魔力”,它充当倒数计时器,它检查问题对用户可见多久,并向下计数直到它自动移动到下一个问题,当duration是大于或等于timeOut (本例中为5秒),则调用nextQuestion方法

但是,你如何使用它你问? 你创build一个Question List ,创build一个QuizPane的实例,并将其添加到屏幕上显示的其他容器,例如…

 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); } }); } } 

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

QuizMaster

 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()); } } } } 

如果您在此应用程序中使用了Jframe,只需检查是否将面板添加到了框架中,然后将标签添加到了面板中,只需检查是否将面板添加到了Jframe中,否则就赢得了不出现