How do i remove and add components from jPanel? Java - java

Ok, i am just a beginner programmer, so i am having a lot of difficulty in figuring this out. Basically i am trying to create a one digit calculator(meaning that calculations only occur with single digits of numbers). I have created the buttons, assigned them action listener and their classes, and all those stuff. And then i try to display those numbers to a label. Now the problem i have is, that, i have a button, which when clicked, will use a class. From that class, what i want to do is, remove all the buttons form the panel, and add new buttons. But when i try to remove the buttons, something weird happens. If i click that button, the buttons instead of getting removed/disappering, they stay there, but i cant interact with them. Any help to fix that? I want to completely remove them from the panel. Then i want to add new buttons in their place.
Here is the code of the main class
package onecalculator;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class code {
static JLabel see = new JLabel("Int a");
static JLabel no = new JLabel("Int b");
static JLabel lol = new JLabel("Answer");
static JPanel area = new JPanel();
static JButton secn = new JButton("next");
static JButton one = new JButton("1");
static JButton two = new JButton("2");
static JButton three = new JButton("3");
static JButton four = new JButton("4");
static JButton five = new JButton("5");
static JButton six = new JButton("6");
static JButton seven = new JButton("7");
static JButton eight = new JButton("8");
static JButton nine = new JButton("9");
static JButton bone = new JButton("1");
static JButton btwo = new JButton("2");
static JButton bthree = new JButton("3");
static JButton bfour = new JButton("4");
static JButton bfive = new JButton("5");
static JButton bsix = new JButton("6");
static JButton bseven = new JButton("7");
static JButton beight = new JButton("8");
static JButton bnine = new JButton("9");
static JButton div = new JButton("div");
static JButton mul = new JButton("mul");
static JButton add = new JButton("add");
public int a;
public int b;
public static void main(String[] args) {
JFrame screen = new JFrame("One Digit Calculator");
screen.setSize(400,600);
screen.setResizable(false);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screen.add(area);
area.add(see);
area.add(no);
area.add(lol);
area.add(secn);
area.add(one);
area.add(two);
area.add(three);
area.add(add);
area.add(four);
area.add(five);
area.add(six);
area.add(mul);
area.add(seven);
area.add(eight);
area.add(nine);
area.add(div);
secn.addActionListener(new secn());
two.addActionListener(new Twoc());
three.addActionListener(new Threec());
four.addActionListener(new Fourc());
five.addActionListener(new Fivec());
six.addActionListener(new Sixc());
seven.addActionListener(new Sevenc());
eight.addActionListener(new Eightc());
nine.addActionListener(new Ninec());
one.addActionListener(new Onec());
area.setLayout(new GridLayout(4,4));
screen.setVisible(true);
}
}
Then here is the code of the class that removes the buttons in the panel
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class secn implements ActionListener {
#Override
public void actionPerformed(ActionEvent arg0) {
code.area.remove(code.one);
code.area.remove(code.two);
code.area.remove(code.three);
code.area.remove(code.four);
code.area.remove(code.five);
code.area.remove(code.six);
code.area.remove(code.seven);
code.area.remove(code.eight);
code.area.remove(code.nine);
code.area.add(code.bone);
code.area.add(code.btwo);
code.area.add(code.bthree);
code.area.add(code.bfour);
code.area.add(code.bfive);
code.area.add(code.bsix);
code.area.add(code.bseven);
code.area.add(code.beight);
code.area.add(code.bnine);
}
}
Please help.

What you would want to do is to call repaint() and revalidate() on the container (i.e. 'JPanel area') that holds you buttons. If you want to know exactly what repaint and revalidate do, have a look at this answer.
Below your code where you add your new buttons inside of the actionPerformed method, add the following to update the container:
code.area.repaint();
code.area.revalidate();
Keep in mind that this will cause your new elements to be added to the end of the elements that weren't deleted and in the order that you add them. You can use GridBagConstraints to select where which button is placed.
But I would say that removing the old buttons just to create new ones only for the purpose of entering the second value seems like a bad idea. Additionally, having separate ActionListeners for each button also seems a little wasteful.
I would propose having a global variable (boolean for example) to indicate whether you're using the first or second value.
static boolean isFirst = true;
When the 'next button' is pressed, you could then change this variable to 'false' and not remove any of the buttons. In your ActionListener you would just look at this variable to know whether to assign the pressed number to value a or value b.
For your ActionListener for the number buttons, I would propose to reuse one for all of them like this:
class MyListener implements ActionListener{
int value;
//when creating new instances of MyListener, you give each listener
//an int equivalent to the buttons value
MyListener(int value){
this.value = value;
}
#Override
public void actionPerformed(ActionEvent arg0){
if(isFirst){ //first value
a = value; //add value to your first number in any way you like
} else { //second value
b = value; //add value to your second number in any way you like
}
}
}
You would assign your ActionListener as follows:
two.addActionListener(new MyListener(2));
three.addActionListener(new MyListener(3));
I hope this is helpful and understandable. There are probably better ways to do it, but this would be my suggestion. I'm open to feedback on this.

The easiest solution would be to create a new JPanel instead of deleting the old buttons.
JPanel newArea= new JPanel();
//Add new buttons
code.area = newArea;
However i think you should consider redesigning your code. First of all you should not use static variables for your code class. Instead make them private and make a objekt of your code class.
public class code{
private JLabel see = new JLabel("Int a");
private JLabel no = new JLabel("Int b");
//...
public static void main(String[] args){
code mainClass = new code();
}
This allows you to use multiple instances of your code class instead of just one. Secondly you should not make a new class for every Actionlistener. Ecspecially since i think they are all doing the same. Also i dont think you even need to remake all the buttons. If you just want to save 2 values you can check if the first one has been set already:
class ButtonListener implements ActionListener{
int buttonValue;
code callingClass;
//Save the buttonn number and the calling class
MyListener(int buttonValue, code callingClass){
this.buttonValue = buttonValue;
this.callingClass = callingClass;
}
//If a is null set a, otherwise set b
public void actionPerformed(ActionEvent arg0){
if(callingClass.a == null){
callingClass.a = buttonValue;
} else {
callingClass.b = buttonValue;
}
}
}
In your code class you should then initialize a and b with null in your constructor, alongside all the stuff you preivously initialized in your main and use the ButtonListener class as your new actionlistener.
public code(){
a = null;
b = null;
//Initialize all the other stuff
secn.addActionListener(new ButtonListener(2, this));
}

Whenever you add or remove elements to a visible Swing component, you should use revalidate() (to recalculate its layout now that children have changed) and repaint() (to repaint the component itself), as suggested by Custos' answer.
However, there is the question of why you need to do this. You have two visually identical sets of digit-buttons, that only differ in how they handle clicks. There is no need to replace the buttons - just handle those differences by keeping a little bit of extra state (hasA and hasB varibales in my code below).
Since the introduction of lambdas (inline, anonymous functions), writing handler code for Java UIs has become much more readable and less verbose: note how my code below has 1-line handlers to bind the interface with actual logic, and how easy it would be to, say, add a new operator to the code below.
Note also that you do not need expose all graphical elements of a class as fields - here, digits and operators are only used to call digitPressed and operatorPressed, and lead to a smaller set of fields in the Calc class.
Also, by making Calc a subclass of JPanel, and avoiding any and all static fields, I can easily create multiple calculators operating side-by-side, independent of each other.
package one;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class Calc extends JPanel {
private JLabel labelA = new JLabel();
private JLabel labelB = new JLabel();
private JLabel labelAnswer = new JLabel();
private int a;
private int b;
private boolean hasA = false;
private boolean hasB = false;
public Calc() {
setLayout(new GridLayout(0, 4));
add(labelA);
add(labelB);
add(labelAnswer);
reset(); // rewrites labels, resets state
JButton reset = new JButton("reset");
add(reset);
reset.addActionListener((e) -> reset());
for (int i=1; i<10; i++) {
JButton b = new JButton("" + i);
add(b);
final int index = i; // for use in lambda
b.addActionListener((e) -> digitPressed(index));
}
for (String op : new String[] {"add", "mul", "div"}) {
JButton b = new JButton(op);
add(b);
final String index = op; // for use in lambda
b.addActionListener((e) -> operatorPressed(index));
}
}
private void reset() {
labelA.setText("value A: ?");
labelB.setText("value B: ?");
labelAnswer.setText("no operator");
hasA = hasB = false;
}
private void digitPressed(int i) {
if ( ! hasA) {
hasA = true;
a = i;
labelA.setText("value A:" + a);
} else if ( ! hasB) {
hasB = true;
b = i;
labelB.setText("value B:" + b);
}
}
private void operatorPressed(String operator) {
String answer = "???";
if (operator.equals("mul")) {
answer = "= " + (a * b);
} else if (operator.equals("div")) {
answer = "= " + (a / b);
} else if (operator.equals("add")) {
answer = "= " + (a + b);
}
labelAnswer.setText(answer);
}
public static void main(String[] args) {
JFrame screen = new JFrame("One Digit Calculator");
screen.setSize(400,600);
screen.setResizable(false);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screen.add(new Calc());
screen.setVisible(true);
}
}

Related

trouble hooking up an actionlistener to a radiobutton

I'm trying to create a personality quiz. The idea is that a JPanel will show up with the first question and then once the user selects one of the radio buttons, the second JPanel with the second question will show up.
Since I have 5 questions each with 3 answer choices I thought it would be faster and more efficient to create a method that creates radio buttons and adds an ActionListener but I'm having trouble getting the listener to work. Right now to see if it works I'm just trying to change the button text when it is selected.
I tried adding the listener to the button in the createButton method but I haven't had luck. Originally I had it as a parameter in the method but that didn't get the expected result so I tried to create it without the listener as a parameter. Inserting the listener as a parameter didn't change the text.
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.WindowConstants;
public class UserInterface extends ClickListener implements Runnable
{
private ActionListener listeners;
private JFrame frame;
public UserInterface(){
}
#Override
public void run() {
frame = new JFrame("title");
frame.setPreferredSize(new Dimension(1000, 1000));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
createComponents(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
public JFrame getFrame(){
return frame;
}
private void createComponents(Container container){
BoxLayout layout = new BoxLayout(container, BoxLayout.Y_AXIS);
container.setLayout(layout);
container.add(QuizIntro());
container.add(QuestionOne());
container.add(QuestionOneGroup());
}
public JLabel QuizIntro(){
JLabel text = new JLabel("Intro text");
return text;
}
public JLabel QuestionOne(){
JLabel text = new JLabel("1. this is the first question");
return text;
}
public JPanel QuestionOneGroup(){
JRadioButton int1 = createButton("This button was created with my createButton method");
JRadioButton e1 = new JRadioButton("This button was created without that method");
JPanel panel = new JPanel();
panel.add(int1);
panel.add(e1);
return panel;
}
public JRadioButton createButton(String text){
JRadioButton b = new JRadioButton(text);
b.addActionListener(listeners);
return b;
}
}
here's my action listener
public class ClickListener implements ActionListener {
private UserInterface ui;
private JRadioButton b;
#Override
public void actionPerformed(ActionEvent ae) {
if (b.isSelected()){
b.setText("this works");
}
}
}
The actual result is that the button is selected but the text does not change. I'm having trouble figuring out if I'm running the wrong test to see if my listener works or if my listener just does not work.
Your main problem is here:
private ActionListener listeners;
You create an ActionListener but you never call it.
You're also inheriting from ClickListener but never using it.
public class UserInterface extends ClickListener implements Runnable
In your code you call:
b.addActionListener(listeners); //Listeners is null!
So, you're telling your JRadioButton to have a listener that is null.
So, here you have 2 approaches:
Remove private ActionListener listeners; from the top of your program and change:
b.addActionListener(listeners);
To:
b.addActionListener(this);
Remove extends ClickListener from your class definition and keep:
b.addActionListener(listeners);
But adding
listeners = new ClickListener();
Before
createComponents(frame.getContentPane());
IMO I would take the 2nd approach.
BTW for your ActionListener to actually change the text you don't need private variables but rather get the source and cast it. For example:
public void actionPerformed(ActionEvent ae) {
JRadioButton b = (JRadioButton) ae.getSource();
b.setText("this works");
}
Forgot to mention, please follow Java naming conventions so that your program is more readable and understandable by everyone who reads it.
firstWordLowerCaseVariable
firstWordLowerCaseMethod()
FirstWordUpperCaseClass
ALL_WORDS_UPPER_CASE_CONSTANT
And indent your code correctly as well :)
First of all the GUI should not extend the listener class. Not good. Better to keep them separate and to pass references where needed. For example, if the listener needs a reference to the GUI, pass it in as a paramter
Also, you apparently want to do is to respond to JRadioButton selection immediately with useful behavior. I would use an ItemListener not an ActionListener, since the ItemListener will tell you if the radio button has been selected. Calling getSource() on the ItemEvent will give you the current JRadioButton that was selected.
e.g.,
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class UserInterface2 extends JPanel {
public static final Object QUESTION = "question";
// fill label with blank text to expand it
private JLabel resultLabel = new JLabel(String.format("%150s", " "));
// CardLayout to allow swapping of question panels
private CardLayout cardLayout = new CardLayout();
private JPanel centerPanel = new JPanel(cardLayout);
public UserInterface2(List<Question> questions) {
centerPanel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
for (Question question : questions) {
centerPanel.add(createQPanel(question), question.getQuestion());
}
JPanel bottomPanel = new JPanel(new BorderLayout());
// add button that allows swapping question panels
bottomPanel.add(new JButton(new AbstractAction("Next") {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.next(centerPanel);
}
}), BorderLayout.LINE_START);
bottomPanel.add(resultLabel);
setLayout(new BorderLayout());
add(bottomPanel, BorderLayout.PAGE_END);
add(centerPanel);
}
private JPanel createQPanel(Question question) {
JPanel radioPanel = new JPanel(new GridLayout(0, 1));
radioPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
ButtonGroup buttonGroup = new ButtonGroup();
ItemListener myItemListener = new MyItemListener(this);
for (String answer : question.getAnswers()) {
JRadioButton answerButton = new JRadioButton(answer);
// this is present in case you want to extract the Question
// object from the JRadioButton, useful for if you want to
// test if the selected answer is correct
answerButton.putClientProperty(QUESTION, question);
// add our listener to the JRadioButton
answerButton.addItemListener(myItemListener);
// add to button group so only one can be selected
buttonGroup.add(answerButton);
// add to JPanel for display
radioPanel.add(answerButton);
}
JPanel qPanel = new JPanel(new BorderLayout());
qPanel.add(new JLabel(question.getQuestion()), BorderLayout.PAGE_START);
qPanel.add(radioPanel);
return qPanel;
}
// public method that the item listener will use to display selection
public void displayResult(String selectedText) {
resultLabel.setText(selectedText);
}
private static void createAndShowGui() {
// create mock questions
// likely this information will be in a text file
List<Question> questions = new ArrayList<>();
for (int i = 0; i < 10; i++) {
String question = "Question " + i;
List<String> answers = new ArrayList<>();
for (int j = 0; j < 4; j++) {
answers.add(String.format("Answer [%d %d]", i, j));
}
int correctIndex = (int) (Math.random() * answers.size());
// future iteration will also need correctIndex int
questions.add(new Question(question, answers, correctIndex));
}
UserInterface2 mainPanel = new UserInterface2(questions);
JFrame frame = new JFrame("User Interface");
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(() -> createAndShowGui());
}
}
class MyItemListener implements ItemListener {
private UserInterface2 ui;
public MyItemListener(UserInterface2 ui) {
this.ui = ui;
}
#Override
public void itemStateChanged(ItemEvent e) {
JRadioButton source = (JRadioButton) e.getSource();
String selected = "The JRadioButton " + source.getText();
selected += e.getStateChange() == ItemEvent.SELECTED ? " has been selected"
: " has been unselected";
// to get the actual Question object get the client property:
Question question = (Question) source.getClientProperty(UserInterface2.QUESTION);
// now can get answer Strings and check if correct one selected
System.out.println(question);
String correctAnswer = question.getAnswers().get(question.getCorrectIndex());;
if (source.getText().equals(correctAnswer)) {
selected += " and is correct";
} else {
selected += " and is incorrect";
}
// tell the GUI to display the result
ui.displayResult(selected);
}
}
class Question {
private String question;
private List<String> answers;
private int correctIndex;
public Question(String question, List<String> answers, int correctIndex) {
this.question = question;
this.answers = answers;
this.correctIndex = correctIndex;
}
public String getQuestion() {
return question;
}
public List<String> getAnswers() {
return answers;
}
public int getCorrectIndex() {
return correctIndex;
}
#Override
public String toString() {
return "Question [question=" + question + ", correctIndex="
+ correctIndex + "]";
}
}
Since there are many things to be improved in your code, I thought I'll write a sample program to show you how this kind of a thing should be done in Swing. Try it and see.
(There are few things we can improve in this sample code as well. But I just wanted to keep things simple and address only the key points.)
import javax.swing.*;
import java.awt.event.*;
public class Questions {
public static void main(String[] args) {
JFrame f = new JFrame("Questions");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new QuestionPanel());
f.setBounds(300, 200, 400, 300);
f.setVisible(true);
}
}
class QuestionPanel extends JPanel implements ActionListener {
private static final String ANSWER_1_TEXT = "Answer 1";
private static final String ANSWER_2_TEXT = "Answer 2";
private static final String ANSWER_3_TEXT = "Answer 3";
private JRadioButton answer1;
private JRadioButton answer2;
private JRadioButton answer3;
QuestionPanel() {
answer1 = new JRadioButton(ANSWER_1_TEXT);
answer2 = new JRadioButton(ANSWER_2_TEXT);
answer3 = new JRadioButton(ANSWER_3_TEXT);
answer1.addActionListener(this);
answer2.addActionListener(this);
answer3.addActionListener(this);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(answer1);
buttonGroup.add(answer2);
buttonGroup.add(answer3);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(answer1);
add(answer2);
add(answer3);
}
#Override
public void actionPerformed(ActionEvent e) {
JRadioButton selectedAnswer = (JRadioButton) e.getSource();
if (selectedAnswer == answer1) {
answer1.setText(ANSWER_1_TEXT + " (selected)");
answer2.setText(ANSWER_2_TEXT);
answer3.setText(ANSWER_3_TEXT);
}
else if (selectedAnswer == answer2) {
answer1.setText(ANSWER_1_TEXT);
answer2.setText(ANSWER_2_TEXT + " (selected)");
answer3.setText(ANSWER_3_TEXT);
}
else if (selectedAnswer == answer3) {
answer1.setText(ANSWER_1_TEXT);
answer2.setText(ANSWER_2_TEXT);
answer3.setText(ANSWER_3_TEXT + " (selected)");
}
}
}
How many programmers do you need to change a lightbulb? 76, 1 to change it and 75 to say how they could've done it better.
You do have bad code practices, but thats usually because of a shaky understanding of the fundamental concepts behind the language design. So I won't comment on how your code is bad for this or that, just explain the basics you should know.
To simplify, an ActionListener is an object that will react on an ActionPerformedEvent. Lets define one, a class called Observer:
Observer has no idea who generated the event, so lets tell him, and if it is a JRadioButton, lets use it as one
public class Observer implements ActionListener{
#Override
public void ActionPerformed(ActionEvent ae){
Object source = ae.getSource();
if (source instanceof JRadioButton)
((JRadioButton) source).setText("this works");
}
Any of your JRadioButton are objects that generates ActionEvents constantly, but nobody we care about is usually watching, when we add an ActionListener we are basically saying: make this ActionListener object observe my behaviour.
So we need someone to be observed and an observer, lets make those in your UI (or a simplified version of it):
Since you are trying to use a global listener, you need to make sure there is one there, observer (our ActionListener) is null for now. Lets instantiate it This time, we know that observer is not null
public class UserInterface implements runnable{
private ActionListener observer new Observer();
//...
public void someMethodToCreateButtons(){
JRadioButton observableButton = new JRadioButton("Created here");
observableButton.addActionListener(observer);
}
And thats it, when you select observableButton, it will change its text to "this works".
Those are the basics, now, I used those names observableButton and Observer for a reason, ActionListeners are based on what is known as the observer design pattern you could pick up a book regarding designs patterns, you wont regret it.
Finally, it seems that you are making things too difficult for yourself, try to simplify your logic. Perhaps making a JPanel containing different sets of buttons and displaying it whenever a condition is true? just spitballing here, but try to keep it simple, hope it helps, good luck!

Java Swing and ActionListeners

I am still new to using Swing and creating GUIs in Java. I was working on a simple test code where the color of a button changes to a random color when it is pressed. Although it works, every time I press the button, it minimizes the previous window and opens a new one and they keep piling up. How will I make it so that this does not happen? Does this occur because I am creating an object in the actionPerformed method? The reason why I have made an object there is to link the Swing class with the separate Action class I have made in order to manipulate the button variable.
Therefore, my two questions are:
How can I prevent multiple windows from appearing and instead have them replace each other?
Is there a way to utilize ActionListener within the same class and would it make things easier?
Any help is greatly appreciated!
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class Swing extends JFrame{
private JFrame f;
private JLabel l;
private JButton b;
private JPanel p;
public Swing(){
test();
}
//*
public JFrame getJFrame(){
return f;
}
public JLabel getJLabel(){
return l;
}
public JButton getJButton(){
return b;
}
public JPanel getJPanel(){
return p;
}
//*
public void test(){
// Frame Setup
f = new JFrame("Frame");
f.setVisible(true);
f.setSize(500, 500);
f.setResizable(true);
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
//
// Panel Setup
p = new JPanel();
p.setVisible(true);
//
// Other
b = new JButton("Button");
l = new JLabel("Label");
b.addActionListener(new Action());
//
// Additions
p.add(b);
p.add(l);
f.add(p); // ***
//
}
public static void main(String[] args){
Swing swing = new Swing();
swing.test();
}
}
final class Action implements ActionListener{
public void actionPerformed(ActionEvent e){
Swing swingObject = new Swing(); //
JButton button = swingObject.getJButton(); //
button.setBackground(randomColor());
}
public Color randomColor(){
Random rn = new Random();
ArrayList<Color> color = new ArrayList<Color>();
color.add(Color.BLUE);
color.add(Color.GREEN);
color.add(Color.RED);
color.add(Color.YELLOW);
color.add(Color.PINK);
color.add(Color.CYAN);
color.add(Color.ORANGE);
color.add(Color.MAGENTA);
int s = color.size();
int random = rn.nextInt(s);
return color.get(random);
}
}
From your listener, you're executing
Swing swingObject = new Swing();
This does what it should do: create a new Swing JFrame. You don't want a new JFrame, so don't call its constructor. From the listener, simply get the button that fired the event, and change its color:
JButton button = (JButton) e.getSource();
button.setBackground(randomColor());
You could also pass the button to modify when creating the listener:
class Action implements ActionListener{
private JButton buttonToUpdate;
public Action(JButton buttonToUpdate) {
this.buttonToUpdate = buttonToUpdate;
}
public void actionPerformed(ActionEvent e){
buttonToUpdate.setBackground(randomColor());
}
}
And, to create it:
b = new JButton("Button");
b.addActionListener(new Action(b));

Why isn't my method adding up the values in my other classes? (sscce included)

So I'm very new to java.
This program runs right now. It's an ordering system for food. It uses tabs with grids in each. Each tabs has its own prices.
My problem is when I runs and compiles the program, it runs but the prices don't add up.
Basically, in the GUI class listed below, there's a method that takes the prices from each tab and adds them up....except it's not doing that. It does appear but the "$0.00" isn't changing.
Like I said it compiles, however, when I do press buttons, I can see the compiler comes up with this error:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
This is the main class:
import javax.swing.JFrame;
public class Pizzamain
{
public static void main (String[] args)
{
JFrame frame = new JFrame ("Pizza Mutt Ordering Service");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new PizzaMuttPanel());
frame.pack();
frame.setVisible(true);
}
}
This class holds all the GUI. It also adds up the prices in each tab. I commented out the other tabs.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.text.NumberFormat;
public class PizzaMuttPanel extends JPanel
{
private JLabel totalLabel;
private double total;
public PizzaMuttPanel()
{
setLayout (new BorderLayout());
JLabel name = new JLabel("Pizza Mutt Ordering Service WOOF! WOOF!");
JPanel namePanel = new JPanel();
namePanel.add(name);
add(namePanel, BorderLayout.NORTH);
JTabbedPane mainTabPane = new JTabbedPane();
mainTabPane.add("Pizza", new PizzaPanel());
// mainTabPane.add("Drinks", new DrinksPanel());
// mainTabPane.add("Specials", new SpecialsPanel());
add(mainTabPane, BorderLayout.CENTER);
total = 0.0;
JPanel totalPanel = new JPanel();
totalPanel.add (new JLabel("Your total is:"));
totalLabel = new JLabel("$0.00");
totalPanel.add(totalLabel);
add(totalPanel, BorderLayout.SOUTH);
}
public void addTotal (double intake)
{
total += intake;
NumberFormat dollars = NumberFormat.getCurrencyInstance();
totalLabel.setText(dollars.format(total));
}
}
This an example of one of the tabs. Btw, ALL the tabs follow this logic. As you can see, in the action section, there's something that adds prices each time a button is pressed then relays the data to the GUI class.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class PizzaPanel extends JPanel
{
private double price;
private final JButton pl;
private final JButton ps;
private final JButton cl;
private final JButton cs;
private final JButton vl;
private final JButton vs;
private PizzaMuttPanel mainPanel;
public PizzaPanel()
{
setLayout (new GridLayout (2, 3));
setBackground (Color.red);
pl = new JButton ("Large Pepperoni: $7");
ps = new JButton ("Small Pepperoni: $4");
cl = new JButton ("Large Cheese: $6.50");
cs = new JButton ("Small Cheese: $3.50");
vl = new JButton ("Large Vegetable: $7");
vs = new JButton ("Small Vegetable: $4");
add (pl);
add (ps);
add (cl);
add (cs);
add (vl);
add (vs);
pl.addActionListener(new ButtonListener());
ps.addActionListener(new ButtonListener());
cl.addActionListener(new ButtonListener());
cs.addActionListener(new ButtonListener());
vl.addActionListener(new ButtonListener());
vs.addActionListener(new ButtonListener());
}
//Here we listen for button presses
private class ButtonListener implements ActionListener
{
public void actionPerformed (ActionEvent e)
{
if (e.getSource().equals(pl))
{
price+=7;
}
if(e.getSource().equals(ps))
{
price+=4;
}
if (e.getSource().equals(cl))
{
price+=6.5;
}
if (e.getSource().equals(cs))
{
price+=3.5;
}
if (e.getSource().equals(vl))
{
price+=7;
}
if (e.getSource().equals(vs))
{
price+=4;
}
//This adds all the prices together.
mainPanel.addTotal(price);
}}}
One way, perhaps over-kill for you, but which is a very useful technique to know and to use is to make the object that holds the information displayed by the JPanel's a "bound property" which allows other classes the ability to listen for and respond to changes in its state.
Specifically:
Give your class that holds the info of interest a SwingPropertyChangeSupport field.
If your class extends JComponent or JPanel (or any class that derives from JComponent), it already has one of these.
Give your class a private count int field
Give this field a public getter and setter method.
In the setter field, call the SwingPropertyChangeSupport's firePropertyChange(String propertyName, int oldValue, int newValue) method with the updated and the old value.
Never change the bound property directly but only through its setter method.
Have any class that wants to listen for changes add a PropertyChangeListener to this class.
If you need a more specific example, consider creating and posting an sscce, and I can show you how to modify it to get this to work.
Edit
Your current code has a problem with references -- you need the sub panels to have a valid reference to the main panel, and you should solve this by passing in a reference.
e.g.
private MainPanel mainPanel;
public SubPanelA(MainPanel mainPanel) {
this.mainPanel = mainPanel;
And then when filling tabs:
mainTabPane.add("A", new SubPanelA(this));
mainTabPane.add("B", new SubPanelB(this));
mainTabPane.add("C", new SubPanelC(this));
This way your tab pane classes can call methods from the main class on a valid reference to the displayed main class.
Edit 2
You state:
I'm a little confused. But I have a reference, right? In the buttonlistener class, first I private PizzaMuttPanel mainPanel; at the top, and then I referenced it as mainPanel.addTotal(price);. Addtotal is in the main GUI class, and holds the prices. Price is the price being taken from the button presses in that specific tab's classes. Are you saying I should change the latter to: mainTabPane.add("A", new SubPanelA(this));
Points:
This: PizzaMuttPanel mainPanel; is not a reference but rather only a declaration of a reference variable. After declaration, it references null, until you give it a valid reference.
You state "and then I referenced it as mainPanel.addTotal(price);", ??? this creates no reference whatsoever, and looks to me will only throw a NPE. To give a variable a reference you must assign the reference to it, meaning you need some statement that begins mainPanel = something goes here.
So what i understand is that you have three different tabs on a jtabbedpane. And every tab is class that extends JPanel. So you want to add an actionlistener to each tab you only have to implement the interface Actionlistener on each panel, or implement on each button.
class tab extends JPanel implements Actionlistener
or if you want a specific actionlistener
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Action here for the button
}
});
And then you want to get the information from each tab you have to make the variable price public or private. But to access a private variable you have to make getters.
public int price;
or for the private version.
private int price;
public int getPrice(){ return price; }
The last one is the preferred way because you cant change the value of the price outside the class that holds that variable.
And for last you want to get the information from all three tabs you can do like this.
public void prices()
{
Tab1 tab1 = new Tab1();
Tab2 tab2 = new Tab2();
Tab3 tab3 = new Tab3();
System.out.println("tab1: " + tab1.price + " tab2: " + tab2.price + " tab3: " + tab3.price);
// or if you use getters
System.out.println("tab1: " + tab1.getPrice() + " tab2: " + tab2.getPrice() + " tab3: " + tab3.getPrice());
}
So where do you declare it?
class tab extends JPanel implements ActionListener
{
/*
* Declare variables here
*/
private int price;
private JLabel label;
/*
* Methods here
*/
}
EDIT
The problems is see is that you are checking if the button is clicked by its text not the variable. And the other thing is that you are giving a new instance of the button listener every time. Instead of creating a new class called ButtonListener we are just implement the interface ActionListener on the PizzaPanel class.
public class PizzaPanel extends JPanel implements ActionListener{
private JButton b1 = null;
private JButton b2 = null;
private int Price;
public int getPrice() {
return Price;
}
public PizzaPanel() {
b1 = new JButton("B1");
b2 = new JButton("B2");
b1.addActionListener(this);
b2.addActionListener(this);
add(b1);
add(b2);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(b1)){
Price++;
System.out.println("Button 1 clicked");
}
if(e.getSource().equals(b2))
{
Price--;
System.out.println("Button 2 clicked");
}
}
}

Java Displaying String on a JTextField after a JButton in a separate class is pressed

Currently working on a school project in which I am making a wheel of fortune replica in Java. I have a panel of JButtons within a class called buttonPanel, and a separate class, wheelGUI, which acts as the class that the program runs through. What I want to happen is when the JButton spin is pressed on the GUI, it assigns a random value from String[] wheelStuff to a String, spinValue,using the method spinWheel which acts as the parameter for the JTextField results, and then displays that random value on the Cyan box in the GUI. In non-technical terms, when the button spin is pressed, display a random value in the Cyan box which acts as the current players' spin value. Here is the code for the class buttonPanel
package wheelOfFortune;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class buttonPanels extends JPanel
implements ActionListener
{
private JButton spin, solve, buyVowel, guess, reset, end, cont;
Color yungMoney = new Color(0, 180, 100);
private static String[] wheelStuff = new String[]{"Bankrupt", "Lose a Turn", "$5000", "$600", "$500", "$300", "$800", "$550", "$400", "$900", "$350", "$450", "$700"};
public buttonPanels()
{
setBackground(yungMoney);
spin = new JButton("Spin!");
spin.addActionListener(this);
solve = new JButton("Solve the Puzzle");
solve.addActionListener(this);
buyVowel = new JButton("Buy a Vowel");
buyVowel.addActionListener(this);
guess = new JButton("Guess a Letter");
guess.addActionListener(this);
reset = new JButton("Reset");
reset.addActionListener(this);
cont = new JButton("Continue");
cont.addActionListener(this);
JPanel buttonPanel = new JPanel(new GridLayout(3, 1, 5, 5));
buttonPanel.setPreferredSize(new Dimension(300,380));
buttonPanel.setBackground(yungMoney);
buttonPanel.add(spin);
buttonPanel.add(guess);
buttonPanel.add(buyVowel);
buttonPanel.add(solve);
buttonPanel.add(cont);
buttonPanel.add(reset);
add(buttonPanel);
}
public void actionPerformed(ActionEvent e)
{
JButton b = (JButton)e.getSource();
b.addActionListener(this);
if(b==spin)
{
wheelGUI.spinWheel(wheelStuff);
}
repaint();
}
}
And here is the code for the main class, wheelGUI
package wheelOfFortune;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.Random;
import javax.swing.*;
public class wheelGUI extends JFrame implements ActionListener {
private playerPlate player1, player2, player3;
Color yungMoney = new Color(0, 180, 100);
private String fileName = "M:/wheelOfFortune/src/wheelOfFortune/img/wheel1.png";
private String cat;
private static String spinValue = "";
private static String[] wheelStuff = new String[]{"Bankrupt", "Lose a Turn", "$5000", "$600", "$500", "$300", "$800", "$550", "$400", "$900", "$350", "$450", "$700"};
private static JTextField results;
public wheelGUI() {
super("Butt Stuff!");
ImageIcon i = new ImageIcon(fileName);
JLabel picture = new JLabel(i);
player1 = new playerPlate("Garrett", Color.RED);
player2 = new playerPlate("Jonny", Color.YELLOW);
player3 = new playerPlate("Robert", Color.BLUE);
buttonPanels buttons = new buttonPanels();
letterBoard letters = new letterBoard();
catBox category = new catBox(cat);
inputField input = new inputField();
Box wall = Box.createHorizontalBox();
wall.add(player1);
wall.add(Box.createHorizontalStrut(5));
wall.add(player2);
wall.add(Box.createHorizontalStrut(5));
wall.add(player3);
JPanel result = new JPanel();
result.setBackground(yungMoney);
JTextField results = new JTextField(spinValue);
results.setBackground(Color.CYAN);
results.setHorizontalAlignment(JTextField.CENTER);
results.setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
results.setPreferredSize(new Dimension(150,100));
results.setFont(new Font("Impact", Font.PLAIN, 28));
results.setEditable(false);
result.add(results);
Box catInput = Box.createVerticalBox();
catInput.add(category);
catInput.add(Box.createVerticalStrut(50));
catInput.add(result);
catInput.add(Box.createVerticalStrut(100));
catInput.add(input);
Container c = getContentPane();
c.setBackground(yungMoney);
c.add(buttons, BorderLayout.EAST);
c.add(wall, BorderLayout.SOUTH);
c.add(letters, BorderLayout.NORTH);
c.add(picture, BorderLayout.WEST);
c.add(catInput, BorderLayout.CENTER);
}
public static String spinWheel(String[] wheelStuff)
{
Random rnd = new Random();
wheelStuff[rnd.nextInt(wheelStuff.length)] = spinValue;
return spinValue;
}
public static void main(String[] args) {
wheelGUI window = new wheelGUI();
window.setBounds(50, 50, 1024, 768);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setResizable(false);
window.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
// logic for any additional panels. other logics should be in individual
// classes.
}
}
Thanks for your help! Note: all of the code in wheelGUI that isn't related to something previously stated can be ignored, it's from various other classes.
The use of static fields in this manner is not recommended.
Instead, the buttonsPanel should be taking a reference of the wheelGUI. This will allow the buttonsPanel to call the required methods on the wheelGUI when it needs to.
A better solution would be to use a model which can sit between the two UIs and model the logic, firing events as parts of the model change
Your primary problem is your shadowing the results textfield. That is, you've declared and class level, static field and then redeclared it within your constructor
Change the following line in your wheelGUI constructor
JTextField results = new JTextField(spinValue);
To
results = new JTextField(spinValue);
This will mean when you set the text on the results field, you will be setting the value of the correct instance of the field.

Issues with GridLayout not working

I am trying to create a 2x2 grid out of 4 buttons for a membership program I am developing. The issue I'm having is that regardless of what I do, it just shows up as a 1x4 grid. Code is as follows.
private void buildStartupPanel()
{
startup = new JPanel();
startup.setLayout(new GridLayout(2,2));
addMember = new JButton ("Add a new member");
removeMember = new JButton ("remove Member");
reviewMember = new JButton ("Review a Member");
reviewAll = new JButton ("Review All Members");
startup.add(addMember);
startup.add(removeMember);
startup.add(reviewMember);
startup.add(reviewAll);
addMember.addActionListener(this);
removeMember.addActionListener(this);
reviewMember.addActionListener(this);
reviewAll.addActionListener(this);
}
When I output the result, it shows the following
Add a new Member
Remove Member
Review A Member
Review all Members
Instead of
Add a new Member Remove A Member
Review A Member Review all Members
Also if anyone could help me put a space between each of the buttons that would be great!
Use the 3rd & 4th int to the constructor for spacing. Otherwise, seems to work just fine here:
import java.awt.GridLayout;
import javax.swing.*;
public class StartupPanel {
private JComponent getStartupPanel()
{
JPanel startup = new JPanel();
startup.setLayout(new GridLayout(2,2,50,5));
JButton addMember = new JButton("Add a new member");
JButton removeMember = new JButton("remove Member");
JButton reviewMember = new JButton("Review a Member");
JButton reviewAll = new JButton("Review All Members");
startup.add(addMember);
startup.add(removeMember);
startup.add(reviewMember);
startup.add(reviewAll);
return startup;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
StartupPanel sp = new StartupPanel();
JOptionPane.showMessageDialog(null, sp.getStartupPanel());
}
});
}
}
thanks for the responses!! Come to find out it was my 2nd panel that I was adding to the code was misspelled (woops) and throwing everything off. Guess that's the importance of posting a full SSCCE. At least I learned how to do the spacing! thanks all!

Categories