I'm writing a program that has a quiz element, and when the user gets an answer wrong, feedback is given. The question JFrame is made of a JLabel that has the actual question, and 4 JRadioButtons that have the different options (named rad1, rad2, rad3, rad4). What I'm trying to do is if the user gets an asnswer wrong, the radio button with the correct answer's background colour turns green and the radio button with the answer that the user gave's background turns red.
Here's the FOR loop that I'm using to figure out which of the answers is correct:
private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) {
System.out.println("Submit Clicked");
//figures out what choice the user selected
String correctAnswer = questions.get(current).getAnswer();
int numChoice = -1;
String choice = "";
boolean answered = false;
if (rad1.isSelected()) {
numChoice = 0;
answered = true;
choice = rad1.getText();
} else if (rad2.isSelected()) {
numChoice = 1;
answered = true;
choice = rad2.getText();
} else if (rad3.isSelected()) {
numChoice = 2;
answered = true;
choice = rad3.getText();
} else if (rad4.isSelected()) {
numChoice = 3;
answered = true;
choice = rad4.getText();
} else { //user didn't pick a choice
JOptionPane.showMessageDialog(null, "You didn't answer the question, try again!");
}
if (choice.equals(correctAnswer)) {
score++;
System.out.println("score++");
} else {
//figures out which of the answers was correct
rad1.setBackground(Color.RED);
for (int i = 0; i < 4; i++) {
if (questions.get(current).getChoices()[i].equals(correctAnswer)) {
System.out.println(correctAnswer);
System.out.println(i);
//me trying to see if it will change if I put it outside the switch
//confirmed that it will not.
rad1.setBackground(Color.RED);
switch (i) {
case 0:
rad1.setBackground(new Color(51, 204, 51));
break;
case 1:
rad2.setBackground(new Color(51, 204, 51));
break;
case 2:
rad3.setBackground(new Color(51, 204, 51));
break;
case 3:
rad4.setBackground(new Color(51, 204, 51));
break;
}
break;
}
}
switch (numChoice) {
case 0:
rad1.setBackground(new Color(153, 0, 0));
break;
case 1:
rad2.setBackground(new Color(153, 0, 0));
break;
case 2:
rad3.setBackground(new Color(153, 0, 0));
break;
case 3:
rad4.setBackground(new Color(153, 0, 0));
break;
}
}
//loads next question
//loads the next question
if (current < 10) {
updateFrame();
} else {
//ends the quiz
}
}
I've been playing around with the .setBackground() method for a while, and if I put print statements in the case blocks, they execute, but the colouring doesn't happen. Is there something dumb that I'm missing?
Thanks
EDIT: Added more code to see that the FOR loop is within the btnSubmitActionPerformed() method. When the user clicks the button, their answer is to be judged and the colour of the radio button is to be changed.
You look to have code that is overly and unnecessarily complex. Myself, I'd try to "OOP-ify" things to reduce cyclomatic complexity and have
A nonGUI Question class,
with a String field for questionText,
with a String field for correctAnswer,
with a List<String> for incorrectAnswers.
I'd give it a method, say public List<String> getShuffledAnswers() to return a List of Strings with all answers, both correct and incorrect, shuffled in their own list,
A boolean method to testAnswer(String test), and return true of the test equals the correctAnswer.
I'd then create a JPanel called QuestionPanel
that has a Question field
that displays the information of a single Question object, including the questionText in the JLabel and all the shuffled answers in JRadioButtons.
It would have methods for getting the selected JRadioButton and for getting the Question,
And a method for setting making a JRadioButtons background non-opaque when need be, by calling `setOpaque(false)
And a method that allows the calling code to set the background of select JRadioButtons with a correct answer color or incorrect answer color.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class TestQuestions extends JPanel {
private static final Question TEST_QUESTION = new Question("Select the Correct Answer", "This answer is correct",
"Incorrect Answer 1", "Incorrect Answer 2", "Incorrect Answer 3");
private QuestionPanel questionPanel = new QuestionPanel();
public TestQuestions() {
questionPanel.setQuestion(TEST_QUESTION);
JButton testAnswerBtn = new JButton(new AbstractAction("Test Answer") {
#Override
public void actionPerformed(ActionEvent e) {
boolean isCorrect = questionPanel.isCorrectAnswerSelected();
String message = "";
if (isCorrect) {
message = "Correct answer selected!";
} else {
message = "Incorrect answer selected!";
}
JOptionPane.showMessageDialog(TestQuestions.this, message);
questionPanel.displayCorrectWrongAnswers();
}
});
JButton clearAllBtn = new JButton(new AbstractAction("Clear All") {
#Override
public void actionPerformed(ActionEvent e) {
questionPanel.clearAll();
questionPanel.clearSelection();
}
});
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 5));
btnPanel.add(testAnswerBtn);
btnPanel.add(clearAllBtn);
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
setLayout(new BorderLayout(5, 5));
add(questionPanel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("TestQuestions");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new TestQuestions());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class QuestionPanel extends JPanel {
private static final Color CORRECT_ANSWER_SELECTED_COLOR = new Color(151, 255, 151);
private static final Color CORRECT_ANSWER_NOT_SELECTED_COLOR = new Color(151,151, 255);
private static final Color INCORRECT_ANSWER_SELECTED_COLOR = new Color(255, 151, 151);
private Question question;
private JLabel questionTextLabel = new JLabel();
private List<JRadioButton> answerButtonList = new ArrayList<>();
private JPanel answerPanel = new JPanel(new GridLayout(0, 1));
private ButtonGroup buttonGroup = new ButtonGroup();
public QuestionPanel() {
setLayout(new BorderLayout());
add(questionTextLabel, BorderLayout.PAGE_START);
add(answerPanel, BorderLayout.CENTER);
}
public void setQuestion(Question question) {
this.question = question;
questionTextLabel.setText(question.getQuestionText());
answerPanel.removeAll();
answerButtonList.clear();
buttonGroup = new ButtonGroup();
for (String answer : question.getShuffledAnswers()) {
JRadioButton rBtn = new JRadioButton(answer);
rBtn.setActionCommand(answer);
answerButtonList.add(rBtn);
buttonGroup.add(rBtn);
answerPanel.add(rBtn);
}
}
public boolean isCorrectAnswerSelected() {
ButtonModel model = buttonGroup.getSelection();
if (model == null) {
return false; // nothing selected
} else {
return question.checkAnswer(model.getActionCommand());
}
}
public void clearAll() {
for (JRadioButton jRadioButton : answerButtonList) {
jRadioButton.setOpaque(false);
jRadioButton.setBackground(null);
}
}
public void clearSelection() {
buttonGroup.clearSelection();
}
public void displayCorrectWrongAnswers() {
clearAll();
for (JRadioButton jRadioButton : answerButtonList) {
if (jRadioButton.isSelected()) {
jRadioButton.setOpaque(true);
if (question.checkAnswer(jRadioButton.getActionCommand())) {
jRadioButton.setBackground(CORRECT_ANSWER_SELECTED_COLOR);
} else {
jRadioButton.setBackground(CORRECT_ANSWER_NOT_SELECTED_COLOR);
}
} else if (question.checkAnswer(jRadioButton.getActionCommand())) {
jRadioButton.setOpaque(true);
jRadioButton.setBackground(INCORRECT_ANSWER_SELECTED_COLOR);
}
}
}
}
class Question {
private String questionText;
private String correctAnswer;
private List<String> incorrectAnswerList = new ArrayList<>();
public Question(String questionText, String correctAnswer, String... incorrectAnswers) {
this.questionText = questionText;
this.correctAnswer = correctAnswer;
for (String incorrectAnswer : incorrectAnswers) {
incorrectAnswerList.add(incorrectAnswer);
}
}
public String getQuestionText() {
return questionText;
}
public String getCorrectAnswer() {
return correctAnswer;
}
public List<String> getShuffledAnswers() {
List<String> answers = new ArrayList<>(incorrectAnswerList);
answers.add(correctAnswer);
Collections.shuffle(answers);
return answers;
}
public boolean checkAnswer(String test) {
return correctAnswer.equalsIgnoreCase(test);
}
}
Two immediate things jump to mind:
Where is the code you posted being called from? Making UI changes outside of the Swing worker thread is sort of undefined. Sometimes the right thing will happen, other times not.
I've never tried to set colors on radio buttons, but it seems likely that they don't have a background.
Try setting some other property, like size, just to see if you get a result. If so, then the problem is that radio buttons don't have a background and you'll have to come up with another design. If nothing happens, then it's probably the first problem.
Related
I have a class called SuggestionField It will show the user a list of items to autocomplete his input. he let of inputs is displayed in a JDialog(suggestionFrame). I need to set the JDialogs position to be below its parent, but I can not get the parents (JTextField) X and Y relative to the screen.
Example of the issue
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Arrays;
public class Runner {
public static void main(String[] args) {
JFrame frame = new JFrame();
String[] items = new String[]{"Tiger", "Wolf", "Car", "Cat", "Space", "Sing", "Scene"};
SuggestionField suggestionField = new SuggestionField(new ArrayList<>(Arrays.asList(items)), frame);
frame.setDefaultCloseOperation(3);
frame.setSize(100, 65);
frame.setLocationRelativeTo(null);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1,1));
panel.add(suggestionField);
frame.add(panel);
frame.setVisible(true);
}
public static class SuggestionField extends JTextField implements DocumentListener, KeyListener {
String[] values = new String[0];
ArrayList<String> displayValues = new ArrayList<>(0);
JDialog suggestionFrame;
JPanel suggestionPanel = new JPanel();
Color backGround = new Color(109, 104, 104, 133);
Color selectBackGround = new Color(109, 104, 104, 133);
Color textColor = new Color(5, 19, 88, 255);
Color selectTextColor = new Color(115, 134, 238, 255);
BoxLayout bl = new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS);
int selectedEntry = 0;
public SuggestionField(ArrayList<String> values, JFrame parentDisplay) {
this.getDocument().addDocumentListener(this);
this.addKeyListener(this);
suggestionFrame = new JDialog(parentDisplay);
suggestionFrame.add(suggestionPanel);
suggestionFrame.setUndecorated(true);
suggestionFrame.setAlwaysOnTop(true);
suggestionFrame.setFocusable(false);
suggestionPanel.setFocusable(false);
bl.maximumLayoutSize(suggestionPanel);
this.values = values.toArray(this.values).clone();
}
public boolean updateSuggestions() {
boolean added = false;
boolean add;
displayValues.clear();
for (int i = 0;i < values.length;i++) {
add = true;
for (int k = 0;k < this.getText().length() && k < values[i].length();k++) {
if (values[i].toUpperCase().charAt(k) != this.getText().toUpperCase().charAt(k)) {
add = false;
break;
}
}
if (add && !values[i].equalsIgnoreCase(this.getText()) && values[i].length() > this.getText().length()) {
added = true;
displayValues.add(values[i]);
}
}
return added;
}
private void updateDisplay() {
suggestionFrame.setSize(this.getWidth(), 16 * displayValues.size());
suggestionPanel.removeAll();
suggestionPanel.setLayout(new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS));
for (int i = 0;i < displayValues.size();i++) {
JLabel a = new JLabel(displayValues.get(i));
if (i == selectedEntry) {
a.setBackground(selectBackGround);
a.setForeground(selectTextColor);
} else {
a.setBackground(backGround);
a.setForeground(textColor);
}
suggestionPanel.add(a);
}
suggestionPanel.revalidate();
suggestionFrame.revalidate();
suggestionFrame.setLocation(this.getX(), this.getY() + this.getHeight());
suggestionFrame.setVisible(true);
}
#Override
public void insertUpdate(DocumentEvent e) {
if (updateSuggestions()) {
updateDisplay();
} else suggestionFrame.setVisible(false);
selectedEntry = 0;
this.requestFocus();
}
#Override
public void removeUpdate(DocumentEvent e) {
if (updateSuggestions()) {
updateDisplay();
} else suggestionFrame.setVisible(false);
selectedEntry = 0;
this.requestFocus();
}
#Override
public void changedUpdate(DocumentEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int ID = e.getKeyCode();
switch (ID) {
case 40:
if (selectedEntry < displayValues.size() - 1) selectedEntry++;
updateDisplay();
break;
case 38:
if (selectedEntry > 0) selectedEntry--;
updateDisplay();
break;
case 10:
this.setText(displayValues.get(selectedEntry));
break;
case 27:
suggestionFrame.setVisible(false);
break;
}
this.requestFocus();
this.grabFocus();
}
#Override
public void keyReleased(KeyEvent e) {
}
}
}
Is there any way that inside the updateDisplay() (line 97) method I could get the absolute position of the JTextField for suggestionFrame
You can use SwingUtilities.convertPointToScreen to do this.
Just pass (x, y+height) and your input field to get the starting point to display your dropdown.
JButton component = new JButton();
Point pt = new Point(component.getLocation());
pt.y += component.getHeight();
SwingUtilities.convertPointToScreen(pt, component);
// pt is now in screen coords... so you can use it to position the pop-up
The full documentation can be found here: https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html
I need to set the JDialogs position to be below its parent..
Use Window.setLocationRelativeTo(Component).
This will just place the JDialog ontop of the JTextFiled
Actually, it's more subtle than that. It will take into account if the dialog would otherwise be off the screen, for example, and move it back on. That is something that would need to be explicitly handled, if basing the position on the 'location on screen'.
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());
}
}
}
}
If you used Jframe for this application, just check to see if you added the panel to the frame, you've just added the label to the panel, just check to see if you've added the panel to the Jframe, otherwise it won't show up
I am having a bit of trouble with the code before. It is doing what I want, but I can only call the method once, if I call it more than that it won't work, essentially it only works once. So right now this ballG is an object. It starts off as White, it sets it to green, has a delay, then sets it back to white, this is perfect. The problem is since I do timerG.start(); and I don't end it, when I call it again it doesn't work. If I do timerG.stop(); it ignores the action event and leaves it green without going back to white. How can I make it so I can call this method multiple times???
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JTextArea;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JTextPane;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.Font;
import java.util.concurrent.TimeUnit;
import javax.swing.JLayeredPane;
import javax.swing.JEditorPane;
public class frame extends JFrame {
private JPanel contentPane;
private JTextField textEntered;
private JTextArea displayT;
private JTextPane textPane;
private JLabel instructions;
private JLabel instructions3;
private JLabel instructions2;
private JLabel ballR;
private JLabel ballG;
private JLabel ballB;
private JPanel panel;
private JTextArea textArea;
private JLabel instructions4;
public String translator(String x)
{
String y = "";
for(int i = 0; i< x.length(); i++)
{
y = y + code(x.charAt(i)) + " ";
}
return y;
}
private String code(char a)
{
switch (a) {
case 'e' : return "R";
case 't' : return "B";
case 'a' : return "G";
case 'o' : return "RB";
case 'i' : return "RG";
case 'n': return "BG";
case 's' : return "R-R";
case 'h' : return "R-B";
case 'r' : return "R-G";
case 'd' : return "R-RB";
case 'l' : return "R-RG";
case 'c' : return "R-BG";
case 'u' : return "B-R";
case 'm' : return "B-B";
case 'w' : return "B-G";
case 'f' : return "B-RB";
case 'g' : return "B-RG";
case 'y' : return "B-BG";
case 'p' : return "G-R";
case 'b' : return "G-B";
case 'v': return "G-B";
case 'k' : return "G-RB";
case 'j' : return "G-RG";
case 'x' : return "G-BG";
case 'q' : return "RB-R";
case 'z' : return "RB-G";
case ' ' : return "RB-B";
case '0' : return "RB-RB";
case '1' : return "RB-RG";
case '2' : return"RB-BG";
case '3' : return "RG-R";
case '4' :return "RG-B";
case '5' : return "RG-G";
case '6' : return "RG-RB";
case '7' : return "RG-RG";
case '8' : return "RG-BG";
case '9' : return "BG-R";
}
return "Z";
}
//Trying to get 1/4 of these methods to work, colorRed, colrGreen, colorBlue,setWhite
private void colorRed()
{
Timer timerR = new Timer(750, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
ballR.setForeground(Color.RED);//or RED, depending
}
});
//timer.setRepeats(false);//don't repeat if you don't want to
timerR.start();
ballR.setForeground(Color.WHITE);
}
private void colorGreen()
{
ballG.setForeground(Color.GREEN);
Timer timerG = new Timer(750, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
ballG.setForeground(Color.WHITE);//or RED, depending
}
});
timerG.setRepeats(false);
timerG.start();
}
private void colorBlue()
{
Timer timerB = new Timer(750, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
ballB.setForeground(Color.BLUE);//or RED, depending
}
});
//timer.setRepeats(false);
timerB.start();
ballR.setForeground(Color.WHITE);
}
private void setWhite()
{
Timer timer = new Timer(750, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
ballB.setForeground(Color.WHITE);
ballR.setForeground(Color.WHITE);
ballG.setForeground(Color.WHITE);
}
});
//timer.setRepeats(false);
timer.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
frame frame = new frame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public frame() {
setTitle("RBG Translator");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 529);
contentPane = new JPanel();
contentPane.setBackground(Color.GRAY);
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
textEntered = new JTextField();
textEntered.setText("Enter String...");
textEntered.setBounds(10, 124, 261, 50);
contentPane.add(textEntered);
textEntered.setColumns(10);
JButton submit = new JButton("Submit");
submit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
ballR.setForeground(Color.WHITE);
ballB.setForeground(Color.WHITE);
ballG.setForeground(Color.WHITE);
String input = textEntered.getText().toLowerCase();
String output = translator(input);
displayT.setText(output);
//colorRed();
colorGreen();
//colorRed();
//colorBlue();
colorGreen();
colorGreen();
}
});
submit.setBounds(281, 125, 138, 50);
contentPane.add(submit);
displayT = new JTextArea();
displayT.setBounds(10, 246, 409, 234);
displayT.setLineWrap(true);
contentPane.add(displayT);
instructions = new JLabel("Please enter a word or a phrase that you would like to be transalted\r\n");
instructions.setBounds(10, 11, 396, 14);
contentPane.add(instructions);
instructions3 = new JLabel("Below is a translated text of the word or phrase submitted");
instructions3.setBounds(10, 205, 409, 34);
contentPane.add(instructions3);
instructions2 = new JLabel("into RBG code. Currently A-Z, 0-9, and space are permitted.");
instructions2.setBounds(10, 24, 396, 34);
contentPane.add(instructions2);
panel = new JPanel();
panel.setBounds(470, 25, 304, 169);
contentPane.add(panel);
ballR = new JLabel("\u2022");
panel.add(ballR);
ballR.setForeground(Color.RED);
ballR.setFont(new Font("Tahoma", Font.PLAIN, 99));
ballB = new JLabel("\u2022");
panel.add(ballB);
ballB.setForeground(Color.BLUE);
ballB.setFont(new Font("Tahoma", Font.PLAIN, 99));
ballG = new JLabel("\u2022");
panel.add(ballG);
ballG.setForeground(Color.GREEN);
ballG.setFont(new Font("Tahoma", Font.PLAIN, 99));
textArea = new JTextArea();
textArea.setLineWrap(true);
textArea.setColumns(4);
textArea.setText("a:G k:G-RB u:B-R 4:RG-B "
+ "b:G-B l:R-RG v:G-G 5:RG-G "
+ "c:R-BG m:B-B w:B-G 6:RG-RB "
+ "d:R-RB n:BG x:G-BG 7:RG-RG "
+ "e:R o:RB y:B-BG 8:RG-BG "
+ "f:B-RB p:G-R z:RB-G 9:BG-R "
+ "g:B-RG q:RB-R 0:RB-RB :RB-B "
+ "h:R-B r:R-G 1:RB-RG "
+ "i:RG s:R-R 2:RB-BG "
+ "j:G-RG t:B 3:RG-R " );
textArea.setBounds(429, 246, 345, 234);
contentPane.add(textArea);
instructions4 = new JLabel("Key\r\n");
instructions4.setBounds(429, 205, 345, 34);
contentPane.add(instructions4);
}
}
(I really don't know what your "codes" are doing, as R-RB doesn't seem to make sense, how do you display R, G and B?)
Okay, you need to start by generating some kind of sequence that you want displayed. This will allow the Timer to act as a pseudo loop and update the UI according to the current sequence set
private String[] sequence;
private int index;
private Timer timer;
//...
public Test() {
//...
JButton submit = new JButton("Submit");
submit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
ballR.setForeground(Color.WHITE);
ballB.setForeground(Color.WHITE);
ballG.setForeground(Color.WHITE);
String input = textEntered.getText().toLowerCase();
String output = translator(input);
displayT.setText(output);
timer.stop();
System.out.println(output);
List<String> firstPass = new ArrayList<>(Arrays.asList(output.split(" ")));
List<String> fullPass = new ArrayList<>(25);
for (String pass : firstPass) {
if (pass.contains("-")) {
String[] parts = pass.split("-");
fullPass.addAll(Arrays.asList(parts));
} else {
fullPass.add(pass);
}
}
sequence = fullPass.toArray(new String[fullPass.size()]);
index = 0;
timer.start();
}
});
Basically, this assumes that each space is a separate sequence to be displayed
Next, you need to setup a single Timer which can play through the sequences
timer = new Timer(750, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ballR.setForeground(Color.WHITE);
ballG.setForeground(Color.WHITE);
ballB.setForeground(Color.WHITE);
if (index < sequence.length) {
String set = sequence[index];
System.out.println(index + " = " + set);
for (char c : set.toCharArray()) {
if ('R' == c) {
ballR.setForeground(Color.RED);
} else if ('G' == c) {
ballG.setForeground(Color.GREEN);
} else if ('B' == c) {
ballB.setForeground(Color.BLUE);
}
}
} else {
timer.stop();
}
index++;
}
});
Okay, so, this takes the next String sequence and breaks it down into invidual elements, by spiting the String on the - character (which is why you "codes" don't make sense to me). It then loops through this set and changes the state of the "balls" accordingly.
The Timercontinues` until there are no more sequences left...
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
I'm working on a calculator application (which I have simplified downed to make it easier to debug). When the user hits '=' the IMPORTANTINT will change to 1. When the users clicks another button the field is supposed to clear with the if then statement in CalculatorEngine:
if(IMPORTANTINT == 1){
System.out.println("Ran the block of code");
parent.setDisplayValue("");
IMPORTANTINT = 0;
System.out.println(IMPORTANTINT);
}
This is done so the user can view the result and then start a new calculation. The textField doesn't want to clear. Does anyone know why this is? Thanks!
CalculatorEngine.java
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JOptionPane;
import javax.swing.JButton;
public class CalculatorEngine implements ActionListener{
Calculator parent;
double firstNum, secondNum;
String symbol;
int IMPORTANTINT = 0;
CalculatorEngine(Calculator parent){
this.parent = parent;
}
public void actionPerformed(ActionEvent e){
JButton clickedButton = (JButton) e.getSource();
String clickedButtonLabel = clickedButton.getText();
String dispFieldText = parent.getDisplayValue();
if(IMPORTANTINT == 1){
System.out.println("Ran the block of code");
parent.setDisplayValue("");
IMPORTANTINT = 0;
System.out.println(IMPORTANTINT);
}
if(clickedButtonLabel == "+"){
firstNum = (Double.parseDouble(parent.getDisplayValue()));
parent.setDisplayValue("");
symbol = clickedButtonLabel;
} else if(clickedButtonLabel == "="){
IMPORTANTINT = 1;
secondNum = Double.parseDouble(parent.getDisplayValue());
double answer = firstNum + secondNum;
parent.setDisplayValue(Double.toString(answer));
} else{
parent.setDisplayValue(dispFieldText + clickedButtonLabel);
}
}
Calculator.java
import javax.swing.*;
import java.awt.GridLayout;
import java.awt.BorderLayout;
public class Calculator {
private JPanel windowContent;
private JPanel p1;
private JPanel sideBar;
private JTextField displayField;
private JButton button8;
private JButton button9;
private JButton buttonEqual;
private JButton buttonPlus;
Calculator(){
windowContent= new JPanel();
BorderLayout bl = new BorderLayout();
windowContent.setLayout(bl);
displayField = new JTextField(30);
windowContent.add("North",displayField);
button8=new JButton("8");
button9=new JButton("9");
buttonEqual=new JButton("=");
buttonPlus = new JButton("+");
p1 = new JPanel();
GridLayout gl =new GridLayout(4,3);
p1.setLayout(gl);
sideBar = new JPanel();
GridLayout gl2 = new GridLayout(5,1);
sideBar.setLayout(gl2);
p1.add(button8);
p1.add(button9);
p1.add(buttonEqual);
sideBar.add(buttonPlus);
windowContent.add("Center", p1);
windowContent.add("East", sideBar);
JFrame frame = new JFrame("Calculator");
frame.setContentPane(windowContent);
frame.pack();
frame.setVisible(true);
CalculatorEngine calcEngine = new CalculatorEngine(this);
button8.addActionListener(calcEngine);
button9.addActionListener(calcEngine);
buttonEqual.addActionListener(calcEngine);
buttonPlus.addActionListener(calcEngine);
}
public void setDisplayValue(String val){
displayField.setText(val);
}
public String getDisplayValue(){
return displayField.getText();
}
public static void main(String[] args)
{
Calculator calc = new Calculator();
}
}
public void setDisplayValue(String val){
SwingUtilities.invokeLater(new Runnable{
#Override
public void run()
{
displayField.setText(val);
}
});
}
You are effectively caching the dispFieldText before entering the IMPORTANTINT if statement block. Therefore the JTextField is being cleared but subsequently then to the cached value in the else block.
if (IMPORTANTINT == 1) {
dispFieldText = ""; // add this
...
}
if (clickedButtonLabel.equals("+")) {
...
} else if (clickedButtonLabel.equals("=")) {
...
} else {
// Field being reset here vvvv
parent.setDisplayValue(dispFieldText + clickedButtonLabel);
}
Make sure to clear the variable. Use String#equals to check String content. The == operator checks Object references.
Aside: Use Java naming conventions, IMPORTANTINT is a modifiable variable so should be importantInt (A boolean typically handles a true/false scenario)
Is there a way to toggle a read-only mode so when you click any object in your window it simply returns what you clicked, ignoring the object's usual event handling? IE, while in this "read-only" mode, if you click on a Button, it simply returns the button, not actually pressing the button. Then I could do something like:
if ("thing pressed" == button) "do this";
else if ("thing pressed" == panel) "do that";
else "do nothing";
Here's my code, its a frame with 3 colored boxes. Clicking the 2nd box, the 3rd box, or the background will display a message. Clicking box 1 does nothing. I like using new mouse adapters so I want to do it this way.
Now what I want is when you click box 1, box 1 is treated as selected (if that helps you get the picture). Then if you click anywhere, including box 1 again, box 1 is deselected and nothing else (meaning that box 2, box 3. or the background's message will display). At that time, only if box 2 or 3 were clicked, they will still not display their normal message but a different message would be displayed.
I'm very sorry if I come off a little short.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Labels {
public static void main(String[] args) {
new Labels();
}
Square l1, l2, l3;
public Labels() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
l1 = new Square();
l2 = new Square();
l3 = new Square();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(120, 150);
frame.setResizable(false);
panel.setVisible(true);
panel.setLayout(null);
l1.setLocation(5, 5);
l2.setLocation(5, 60);
l3.setLocation(60, 5);
l2.setColor("yellow");
l3.setColor("black");
l1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//do nothing
}
});
l2.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 2");
}
});
l3.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 3");
}
});
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("pushed background");
}
});
frame.add(panel);
panel.add(l1);
panel.add(l2);
panel.add(l3);
}
class Square extends JLabel{
Color color = Color.blue;
public Square() {
// TODO Auto-generated constructor stub\
setVisible(true);
setSize(50,50);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(color);
g.fillRect(0, 0, 50, 50);
}
public void setColor(String color){
if (color == "white") this.color = Color.white;
else if (color == "black") this.color = Color.black;
else if (color == "yellow") this.color = Color.yellow;
else {
System.out.println("Invalid color");
return;
}
repaint();
}
}
}
Don't disable anything. Simply change the state of your class, perhaps by using a few boolean flag variables/fields and change these flags depending on what is pressed.
So have boolean fields called label1PressedLast, label2PressedLast, and label3PressedLast or something similar, and when a label is pressed, check the states of all other flags and have your program's behavior change depending on the state of these flags and the label that was just pressed. Then set all flags to false except for the one corresponding to the label that was just pressed.
For example, this little program reacts only if the first and then the third JLabel have been pressed:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class FlagEg extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean[] flags = new boolean[LABEL_COUNT];
public FlagEg() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
flags[i] = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
// check if first label and then third pressed:
if (flags[0] && index == 2) {
System.out.println("first and then third label pressed!");
}
// reset all labels and flags to initial state
inactivateAll();
// set pressed label background color and set flag of label just pressed
labels[index].setBackground(Color.pink);
flags[index] = true;
}
private static void createAndShowGui() {
FlagEg mainPanel = new FlagEg();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Logic iteration two: only label 1 is the "primer" JLabel. This is actually easier to implement, because now you only need one boolean flag, that representing label 1 being pressed:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class FlagEg2 extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean label1Flag = false;
public FlagEg2() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
label1Flag = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
if (label1Flag) {
if (index == 1) {
System.out.println("Label 1 and label 2 pressed");
} else if (index == 2) {
System.out.println("Label 1 and label 3 pressed");
}
}
// reset all labels and flags to initial state
inactivateAll();
// if label1, then activate it
if (index == 0) {
labels[0].setBackground(Color.pink);
label1Flag = true;
}
}
private static void createAndShowGui() {
FlagEg2 mainPanel = new FlagEg2();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
package javaapplication6;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* #author Jan Vorcak <vorcak#mail.muni.cz>
*/
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
MouseListener listener = new MouseAdapter() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JLabel) {
count++;
if (count >= 2) {
System.out.println("clicked 2 times on labels");
count = 0;
}
} else {
count = 0;
}
}
};
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
l1.addMouseListener(listener);
l2.addMouseListener(listener);
l3.addMouseListener(listener);
frame.addMouseListener(listener); // or panel.addMouseListener(listener);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
You could want to create a listener that do the job for using the putClientProperty method of JComponent.
public class JComponentClickCountListener extends MouseAdapter {
private final Integer ONE = 1;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object property = jComponent.getClientProperty(JComponentClickCountListener.class);
if (property instanceof Number) {
property = ONE + ((Number) property).intValue();
}
else {
property = ONE;
}
jComponent.putClientProperty(JComponentClickCountListener.class, property);
}
}
}
Then in your code you can decide to have a single instace of that class for all of your components or create a new one each time.
This could give you the advantage of using the propertyChangeListener for future actions.
PS.
The code example do not represent all logic for OP question but i could by used as solid base. Later on i will try to update it. To cover that.
EDIT2:
I think that you should separate the logic, of selection and action over selected items. Then the task is divided into two tasks. First is the possibility to store the information about it state, clicked active, clicked again inactive. The second tasks it to operate on that status when a jComponent status was changed.
This is an simple example that i wrote, the functionality is to highlight the background of labels when the are selected and remove it when it was clicked again or the panel was clicked remove all selections.
This example is divided to three elements Enum, Iterface and class that manage the logic of selection
Enum - we store the possible statuses and a property key.
public enum JComponentActivationStatus {
NONE,
ACTIVE,
INACTIVE;
public static final String PROPERTY_KEY = JComponentActivationStatus.class.getCanonicalName();
}
Interface - provide a delegate for action to be taken when jcomponenet status change.
public abstract interface JComponenetActivationStatusChangeAction<T extends JComponent> {
public abstract void onActivation(T object);
public abstract void onDeactivation(T object);
}
Class - This class mange the status logic of jcomponents.
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
public class JComponenetActivationManager {
public static <T extends JComponent> T addMouseStatusControl(T jComponent) {
jComponent.addMouseListener(new JComponentMouseStatusModyfier());
return jComponent;
}
public static <T extends JComponent> T addActivationStatusChangeAction(T jComponenet, JComponenetActivationStatusChangeAction<T> statusChangeAction) {
jComponenet.addPropertyChangeListener(craeteJCompositeActivationStatusChangeListener(statusChangeAction));
return jComponenet;
}
public static <T extends JComponent> PropertyChangeListener craeteJCompositeActivationStatusChangeListener(JComponenetActivationStatusChangeAction<T> action) {
return new JComponentStatusPropertyChangeListener<T>(action);
}
/**
* Class that set the status for the JComponet after doubClicl
*/
private final static class JComponentMouseStatusModyfier extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object propertyValue = jComponent.getClientProperty(JComponentActivationStatus.PROPERTY_KEY);
if(JComponentActivationStatus.ACTIVE.equals(propertyValue)) { //We check that the ACTIVE status is already selected, if so we inactive.
propertyValue = JComponentActivationStatus.INACTIVE; //If so we inactive it.
} else {
propertyValue = JComponentActivationStatus.ACTIVE; // Otherwise we set it as active
}
jComponent.putClientProperty(JComponentActivationStatus.PROPERTY_KEY, propertyValue); // We use the property key form status
}
}
}
/**
* Help class that fire the actions after status is changed
*/
private static final class JComponentStatusPropertyChangeListener<T extends JComponent> implements PropertyChangeListener {
private final JComponenetActivationStatusChangeAction<T> statusChangeAction;
/**
*
*/
public JComponentStatusPropertyChangeListener(JComponenetActivationStatusChangeAction<T> statusChangeAction) {
if(statusChangeAction == null) {
throw new IllegalArgumentException("action can not be null at this point");
}
this.statusChangeAction = statusChangeAction;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if(JComponentActivationStatus.PROPERTY_KEY.equals(evt.getPropertyName())) {
if(JComponentActivationStatus.ACTIVE.equals(evt.getNewValue())) {
statusChangeAction.onActivation((T) evt.getSource());
}
if(JComponentActivationStatus.INACTIVE.equals(evt.getNewValue())){
statusChangeAction.onDeactivation((T) evt.getSource());
}
}
}
}
}
That class contain two public static method, that allow the developer to add the functionality to mange status to any jComponent object, add subscribe the action handler if any change occur.
At the end we have the main method that test our solution
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
panel.setBackground(Color.CYAN);
addMouseStatusControl(panel);
addMouseStatusControl(l1);
addMouseStatusControl(l2);
addMouseStatusControl(l3);
JComponenetActivationStatusChangeAction<JLabel> activeBackground = new JComponenetActivationStatusChangeAction<JLabel>() {
#Override
public void onActivation(JLabel object) {
object.setOpaque(true);
object.setBackground(Color.YELLOW);
}
#Override
public void onDeactivation(JLabel object) {
object.setOpaque(false);
object.setBackground(object.getParent().getBackground());
}
};
JComponenetActivationStatusChangeAction<JPanel> deactivateChildrens = new JComponenetActivationStatusChangeAction<JPanel>() {
#Override
public void onDeactivation(JPanel object) {
}
#Override
public void onActivation(JPanel object) {
for(Component component : object.getComponents()) {
if(component instanceof JComponent) {
((JComponent) component).putClientProperty(JComponentActivationStatus.PROPERTY_KEY,JComponentActivationStatus.INACTIVE);
}
}
}
};
addActivationStatusChangeAction(l1, activeBackground);
addActivationStatusChangeAction(l2, activeBackground);
addActivationStatusChangeAction(l3, activeBackground);
addActivationStatusChangeAction(panel, deactivateChildrens);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
The solution is very flexible and extendable in case you will need to add more labels.
The example is for those that want to learn. Any comment would be appreciate.