Why will this hailstone number producer not work? - java

I am trying to make a JButton on a JPanel that, when clicked, produces the hailstone number (if the number is even divide by two; if the number is odd, multiply by 3 and then add one). However, when I click my JButton, the program freezes and no number is outputted. Why is this happening?
Here is my code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Panel03 extends JPanel {
private JLabel label;
private JButton button;
private JTextField box;
public Panel03()
{
box = new JTextField("0", 10);
box.setForeground(Color.black);
box.setHorizontalAlignment(SwingConstants.RIGHT);
add(box);
label = new JLabel();
label.setFont(new Font("Serif", Font.BOLD, 20));
label.setForeground(Color.blue);
add(label);
button = new JButton("Next");
button.addActionListener(new Listener());
add(button);
}
private class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String s = box.getText();
int a = Integer.parseInt(s);
int b = a;
do {
if(a%2 == 0){b/=2.0;}
else{a=((3*b)+1);}
label.setText(Integer.toString(b));
}
while(b!= 4||b!= 2||b!= 1);
}
}
}

Whenever a program freezes, a infinite loop is a prime suspect. In this case, adding this line to the loop
System.out.printf("* a=%d b=%d\n",a,b);
shows that a=0 and b=0 forever. Since a is zero, a%2=0, and b/=2 is still zero, so the variables never change. Starting with a=1 still gets stuck at a=4, b=0. In fact for any number I entered, b goes to zero and the loop gets stuck. I am not sure what you are trying to do here, but I suspect your loop exit logic should be and instead of or, and needs to catch the b=0 possibility:
while (b!=4 && b!=2 && b!=1 && b!=0);

Related

How to setText of 2d array of JTextFIelds using actionlistener?

My assignment is to create 2 JPanels, one for a 2d array, 6 rows of 5 empty JTextFields and another for JButtons that should somewhat resemble an on-screen keyboard. I created the JPanels with the empty JTextFields and JButtons, now I need a way so that when I press a JButton with a letter from the alphabet it will assign that letter to the first available JTextField on the first available row and move one column at a time until the whole row is filled with letters (trying to add letters to a full row should do nothing). I also need to create a backspace button which I have that will remove the last letter (pressing backspace on an empty row should do nothing).
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Wordle extends JFrame implements ActionListener
{
private JPanel p1;
private JPanel p2;
private JTextField [][] g;
public Wordle()
{
setSize(500,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
p1 = new JPanel();
p1.setLayout(new GridLayout(6, 5));
g=new JTextField [5][6];
for(int r=0; r<g.length; r++)
{
for(int c=0; c<g[r].length; c++)
{
g[r][c]= new JTextField();
getContentPane().add(g[r][c]);
p1.add(g[r][c]);
}
}
p2 = new JPanel();
p2.setLayout(new GridLayout(4, 7));
JButton a= new JButton("A");
a.addActionListener(this);
p2.add(a);
JButton b= new JButton("B");
b.addActionListener(this);
p2.add(b);
JButton c= new JButton("C");
c.addActionListener(this);
p2.add(c);
JButton d= new JButton("D");
d.addActionListener(this);
p2.add(d);
JButton e= new JButton("E");
e.addActionListener(this);
p2.add(e);
JButton f= new JButton("F");
f.addActionListener(this);
p2.add(f);
JButton g= new JButton("G");
g.addActionListener(this);
p2.add(g);
JButton h= new JButton("H");
h.addActionListener(this);
p2.add(h);
JButton i= new JButton("I");
i.addActionListener(this);
p2.add(i);
JButton j= new JButton("J");
j.addActionListener(this);
p2.add(j);
JButton k= new JButton("K");
k.addActionListener(this);
p2.add(k);
JButton l= new JButton("L");
l.addActionListener(this);
p2.add(l);
JButton m= new JButton("M");
m.addActionListener(this);
p2.add(m);
JButton n= new JButton("N");
n.addActionListener(this);
p2.add(n);
JButton o= new JButton("O");
o.addActionListener(this);
p2.add(o);
JButton p= new JButton("P");
p.addActionListener(this);
p2.add(p);
JButton q= new JButton("Q");
q.addActionListener(this);
p2.add(q);
JButton r= new JButton("R");
r.addActionListener(this);
p2.add(r);
JButton s= new JButton("S");
s.addActionListener(this);
p2.add(s);
JButton t= new JButton("T");
t.addActionListener(this);
p2.add(t);
JButton u= new JButton("U");
u.addActionListener(this);
p2.add(u);
JButton v= new JButton("V");
v.addActionListener(this);
p2.add(v);
JButton w= new JButton("W");
w.addActionListener(this);
p2.add(w);
JButton x= new JButton("X");
x.addActionListener(this);
p2.add(x);
JButton y= new JButton("Y");
y.addActionListener(this);
p2.add(y);
JButton z= new JButton("Z");
z.addActionListener(this);
p2.add(z);
JButton BackSpace= new JButton("<-");
BackSpace.addActionListener(this);
p2.add(BackSpace);
JButton Enter= new JButton("[");
Enter.addActionListener(this);
p2.add(Enter);
this.getContentPane().add(p1,BorderLayout.NORTH);
this.getContentPane().add(p2,BorderLayout.SOUTH);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e) //need help with
{
if(e.getSource().equals("A"))
{
for(int r=0; r<g.length; r++)
{
for(int c=0; c<g[r].length; c++)
{
g[r][c].setText("A");
}
}
}
}
public static void main(String[] args)
{
new Wordle();
}
}
This is my code and my main issue is setting the text in the 2d array of JTextFields using actionlistener, I don't mind doing each if loop for each letter individually as long as it makes sense and works the way I intended it to, btw if you haven't already noticed this is the game "Wordle" I am trying to make, I'm still a newbie programmer and all of this is new to me so some kind of intuitive explanation would be much appreciated.
This line if(e.getSource().equals("A")) is your first issue. It simply will not work because e.getSource() returns an object that will never equal "A".
Instead, you need to first cast the source to the correct object type like so:
public void actionPerformed(ActionEvent e) //need help with
{
JButton clicked = (JButton)e.getSource();
Now we can now get the text from the button using use getText() like this (Although there is a smarter solution we can use down below):
public void actionPerformed(ActionEvent e) //need help with
{
JButton clicked = (JButton)e.getSource();
if(clicked.getText().equals("A")) {
...
The next issue you face is how you store the data, you need to track the current cell using a class variable, rather than just filling up the whole array:
private int row = 0;
private int column = 0;
Then all together the changes might look a bit like this:
//Class variables
private int row = 0;
private int column = 0;
public void actionPerformed(ActionEvent e) //need help with
{
//Cast the event source to a button
JButton clicked = (JButton)e.getSource();
//Handle backspace
if(clicked.getText().equals("<-"))
{
//Decrease the tracking number
column--;
//Make sure that only valid cells are back spaced
if(column < 0)
{
column = 0;
}
//Set the cell to be blank
g[row][column].setText("");
}
//Handle enter
else if(clicked.getText().equals("["))
{
//Add your behaviour here
....
//Finally move to the next row
row++;
}
//Handle the rest of the letter buttons here
else
{
//We don't need `for` loops, we can just directly add the letter from
//the clicked button to the correct place in the array like so
g[row][column].setText(clicked.getText);
//Increment the tracking numbers
column++;
//Move to the next row when the column value is over 5:
if(column > 4)
{
//Move to the next row
row++;
//Reset the column number to 0
column = 0;
}
}
}

How to sum the similar threads inside ExecutorService in java?

I'm trying to sum the similar threads inside the ExecutorService. I think I should use join() method, but I wasn't able to do it correctly.
My Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Gui extends JFrame implements ActionListener{
private JTextField txtSeats, txtAgents, txtTime;
public int numFirst, numSecond, numThird;
private JTextArea [] bookSeat;
private JButton btn1, btn2;
private String output, name;
private Random ran;
private JPanel p1, p2;
private String [] agentNumber;
private int [] sum;
private int x, z;
public Gui() {//A constructor for the Gui class.
ran = new Random();//Initializing the Random class.
setLayout(new BorderLayout());//Setting the layout for the JFrame.
setTitle("");//the title of the program.
p1 = new JPanel();//Creating the first JPanel to add JButtons and JTextField on it.
p1.setLayout(new FlowLayout());//Setting the first JPanel's layout.
//Creating 3 JTextField with a title for each, and adding each of them to the first JPanel.
txtSeats = new JTextField("Number of seats");
p1.add(txtSeats);
txtAgents = new JTextField("Number of agents");
p1.add(txtAgents);
txtTime = new JTextField("Max waiting time");
p1.add(txtTime);
//Creating 2 JButton with a title for each, and adding each of them to the first JPanel.
btn1 = new JButton("Create seats");
p1.add(btn1);
btn2 = new JButton("Book");
p1.add(btn2);
//Registering the 2 JButton to the ActionListener so they work with the actionPerformed() method when they get clicked.
btn1.addActionListener(this);
btn2.addActionListener(this);
add(p1, BorderLayout.NORTH);//Adding the first JPanel to the main JFrame at the position NORTH.
//Giving some properties to the JFrame layout.
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
}//End of the constructor.
public void createSeat() {//A method for creating a number of empty seats depending on the users wish.
//Storing the value of the first JTextField into an integer variable.
//With removing the spaces in it if there is any using "trim()".
numFirst = Integer.parseInt(txtSeats.getText().trim());
//Creating a JTextArea array for the number of seats the user wants to add.
//The size is entered by the user.
bookSeat = new JTextArea[numFirst];
p2 = new JPanel(); //Creating the second JPanel to add JTextArea on it.
p2.setLayout(new FlowLayout()); //Setting the second JPanel's layout.
//for-loop for adding the new JTextArea array into the second JPanel.
//With setting their title and background color and other properties.
for(int i = 0; i < numFirst; i++) {
bookSeat[i] = new JTextArea("Not booked");
bookSeat[i].setBackground(Color.WHITE);
bookSeat[i].setEditable(false);
p2.add(bookSeat[i]);
add(p2, BorderLayout.CENTER);//Adding the second JPanel to the main JFrame at the position CENTER.
setVisible(true);
}
}//End of createSeat() method.
#Override
public void actionPerformed(ActionEvent e) {//A method Overrode from the ActionListener interface.
if(e.getSource().equals(btn1)) {//First case: The JButton "Create seats" is clicked.
//Calling the createSeat() method; to create an empty seats based on the entered number of seats in the first JTextField.
createSeat();
//This method called on a container once new components are added or old ones removed.
//This call is an instruction to tell the layout manager to reset based on the new component list.
//revalidate() will trigger a call to repaint what the component thinks are 'dirty regions.'
//https://stackoverflow.com/questions/1097366/java-swing-revalidate-vs-repaint
revalidate();
}
else if(e.getSource().equals(btn2)) {//Second case: The JButton "Book" is clicked.
//Storing the values of the second and third JTextField into 2 integer variables.
//With removing the spaces in them if there is any using "trim()".
numSecond = Integer.parseInt(txtAgents.getText().trim());
numThird = Integer.parseInt(txtTime.getText().trim());
//Creating an ExecutorService object with fixed thread pool with maximum number of agents threads.
ExecutorService executor = Executors.newFixedThreadPool(numSecond);
//for-loop for the number of times the ExecutorService (thread pool) should run.
//It will keep creating threads until it reaches the maximum number of seats.
for(int i = 0; i < numFirst; i++) {
int count = i;
//Submitting Runnable task to the executor.
executor.execute(new Runnable() {
#Override
public void run() {//A method Overrode from the Runnable interface.
try {
//Getting the name of the current thread & store it in a String variable.
//Then we will have a long name like: pool-1-thread-n (n is a changing thread number).
//We use split("-"); to split it to 4 parts and we take the 4 part which is in position [3].
name = Thread.currentThread().getName();
agentNumber = name.split("-");
//Setting the new text and background color to the JTextArea array after they get booked.
bookSeat[count].setText("Booked by Agent " + agentNumber[3]);
bookSeat[count].setBackground(Color.RED);
revalidate();
//Generating random number between 0 and the waiting time entered by the user.
//And inserting this value inside the sleep() method. For a waiting time between each thread.
x = ran.nextInt(numThird + 1);
Thread.currentThread().sleep(x);
}
catch (Exception e) {}
}//End of run() method.
});//End of the executor.
}//End of the for-loop.
//Temporary solution for finding the similar elements in the array and return the sum for each
//equal threads. BUT NOT WORKING!!!
z = 0;
for(int i = 0; i < numFirst;i++) {
for(int j = 0; j < numFirst; j++) {
if(bookSeat[i].getName().equals(bookSeat[j].getName())) {
sum[i] = z + 1;
}
}
}
//for-loop for storing the booked seats in a String variable.
output = "";
for(int i = 1; i <= numSecond; i++) {
String allAgents = String.valueOf(i);
output += ("\n" + "Agent " + allAgents + " booked " + sum + " seats.");
}
//Displaying a Message Dialog with the information of each agent's booking.
JOptionPane.showMessageDialog(this,output);
//Shutdown the executor.
executor.shutdown();
//Wait until all tasks are finished.
while(!executor.isTerminated()) {
System.out.println("The booking has finished");
}
}//End of else if condition.
}//End of actionPerformed() method.
public static void main(String [] args) {//Main method.
new Gui(); //Calling the Gui.
}//End of Main method.
}//End of the class.
As you may see, I have tried with doing a nested for loop for checking the elements in the array if they were similar and return the sum of them. But this still didn't work!
z = 0;
for(int i = 0; i < numFirst;i++) {
for(int j = 0; j < numFirst; j++) {
if(bookSeat[i].getName().equals(bookSeat[j].getName())) {
sum[i] = z + 1;
}
}
}
Using Executor you loose direct access to the Thread instance which is needed for waiting for it (e.g. join()).
First create an array of Threads, then start them, then join() them. E.g.:
int numSecond = 3;
Runnable r = new Runnable() {
#Override
public void run()
{
// do your stuff inside thread
}//End of run() method.
};
IntStream.range(1,numSecond + 1).forEach( i -> {
Thread t = new Thread( r, "pool-1-thread-" + i);
t.start();
try
{
t.join();
}
catch ( InterruptedException t1 )
{
System.err.println( "thread interrupted" );
}
});
Above example names the thread as you expect in your code:
"pool-1-thread-" + i
If you are only interested in the numer then just pass in
"" + i
Using CompletableFutures might solve the problem also in an elegant way.
BTW1: Using a sleep(x) at the end of the thread's run method is absolutely senseless. It does not delay the start time of another thread. Why at all you want to do this? If you want to execute the logic in run() in a sequential way, there is no need to create use threads at all.
BTW2: Next time you asking something please boil down your code to a minimum which shows your problem. There is no need to setup a whole GUI with buttons, klick handlers etc.
BTW3: The access to shared (global) variables from inside the threads must be synchronized.

Coin flip simulation: Counting heads / tails

I am trying to simulate a coin flipper using Java and window builder. I have a button called "Flip" when that button is pressed the coin image changes depending on which number the random generator I created generates.
I now am trying to figure out a way to display the number of times the coin lands a heads or a tails in their respective JTextFields. I was thinking of using a counter, but I am struggling with how to update that into the text field, so far it only puts in that I have flipped each coin once.
I am very new to programming so any advice or guidance is much appreciated.
// this button flips the coin
btnFlip = new JButton("Flip");
btnFlip.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int headCounter = 0;
int tails = 0;
// this implements the random flip of the coin when the checkbox Run Multiple
// flips is unchecked
if (chckbxNewCheckBox.isSelected() == false) {
Random r = new Random();
int flipper = r.nextInt(2);
if (flipper == 1) {
lblImages.setIcon(new ImageIcon(FinalPrep.class.getResource("/finalPrep/heads.png")));
textFieldHeads.setText(String.valueOf(headCounter));
} else {
lblImages.setIcon(new ImageIcon(FinalPrep.class.getResource("/finalPrep/tails.png")));
textFieldTails.setText(String.valueOf(tails));
}
}
}
});
btnFlip.setFont(new Font("Tahoma", Font.PLAIN, 15));
panel_1.add(btnFlip);
You have to create something like 2 layers. One that contains the window itself along with the title (think of it like a receptor) then you have to create another "layer" that contains the buttons and the textfields. Something like this:
import javax.swing.*;
public class MyFrame extends JFrame {
private JPanel panel;
private JTextField textField;
private JButton button;
public MyFrame(){
panel = new JPanel(); //Step 1. Creation of a receptor
tfCount = new JTextField(10); //Step 2.
button = new JButton("Press Me"); //Creation of buttons & textfields.
panel.add(tfCount); //Step 3.
panel.add(button); //Add those graphics to the receptor
this.setContentPane(panel); //Step 4 Adjust the receptor to the object
this.setVisible(true);
this.setSize(400, 400);
this.setTitle("My 1st GUI!");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
(Sorry for the random example but you did not post any sample of code so I am trying to explain as best as I can)
Then you should put a "flag" depending of the number that was generated for example:
boolean flag = true; //Flag
int counter = 0; //The counter for the heads
int x; //The number you received from the generator
int counter_b; //The counter for the tails
if(x%2 == 0){ //If the number is even
flag = false; // You set down the flag
counter++; //And counter raises its value
}
else
counter_b++;
tfCount.setEditable(true); //This makes your textfield editable
tfCount.setText(String.valueOf(counter)); //And this prints the counter's value
Keep in mind that the counters for the tails and the heads are just examples for you to understand how to use "flags". I hope that I was able to help you because I am new too!! :)

Basic GUI java program in Eclipse keeps crashing

I am trying to make a game and in said game, there are 21 sticks and each person takes turns taking 1-4 sticks until there are no sticks left, if you cant take anymore sticks you lose. I have successfully made this program in eclipse but now I want to add GUI to it so I have to change the code. This code isn't complete but it crashes whenever I press the Go button which is my actionListener. I would type in a number to the text field, press go and it will just crash. How can I fix this?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sticks extends JFrame {
JButton Go;
JTextField tf1, tf2;
static JTextField sttf;
JLabel startTake;
static JLabel errorTake;
JLabel uTake;
JLabel compTake;
public Sticks() {
setLayout(new GridLayout(5, 2, 5, 5));
startTake = new JLabel("How many sticks do you want to take? (1-4)");
add(startTake);
sttf = new JTextField();
add(sttf);
errorTake = new JLabel("Hello");
add(errorTake);
Go = new JButton("Go");
add(Go);
uTake = new JLabel("");
add(uTake);
compTake = new JLabel("");
add(compTake);
// tf1 = new JTextField();
// add(tf1);
// TakeP = new JLabel("One stick taken");
// add(TakeP);
event e = new event();
Go.addActionListener(e);
}
public static class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
int numSticks = 21;
int numToTake = 0;
int randomNum = 0;
while (numSticks > 0) {
try {
int num = (int) (Double.parseDouble(sttf.getText()));
int NumSticks = numSticks - num;
errorTake.setText("There are: " + numSticks + " left");
Robot Rob = new Robot();
numToTake = (int)Math.random() * 4 + 1;
errorTake.setText("There are: " + numSticks + " left");
}
catch (Exception ex) {
ex.printStackTrace();;errorTake.setText("There is a problem");
}
}
}
}
public static void main(String[] args) {
Sticks gui = new Sticks();
gui.setDefaultCloseOperation(EXIT_ON_CLOSE);
gui.setVisible(true);
gui.setSize(600, 200);
gui.setTitle("Nice Game");
}
}
Everytime you click the "Go" button, your actionPerformed fires, and it doesn't wait for user input at all. This is your problem line.
public static class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
//...
int num = (int) (Double.parseDouble(sttf.getText()));
//...
}
}
sttf.getText() always returns "" because sttf is empty, the program doesn't wait for user input unlike Scanner(System.in).
Makes sense that you don't get any Exceptions because it just runs and finishes the game without giving the user enough time to input anything. Are you sure the console doesn't print "Numbers only!", though? Because I've never tried to parse an empty String before.
Okay I've been reading this all wrong, sorry.
Your actionListener generates a new game everytime you click it, because you set your numSticks to 21 at each click. Looking forward, I don't think that's a good idea unless you want to take the same amount of sticks the whole way until the game ends. Same thing stands. If you input a value in sttf, the program won't wait for you to change it because it'd keep using that value until your while loop ends.

Add values when JCheckBox clicked

My program contains a label with the caption “Choose a coffee” and four check boxes – Americano, Espresso, Double Espresso and Latte. Note: A JCheckBox array is recommended here)
I need to add event handling code to allow the user to purchase one or more items. The bill amount is displayed in a label after the user has made their selections.
The prices are Americano €3.75, Espresso €4.00, Double Espresso €4.50 and Latte €3.50. An array is suitable here also.
As the user makes a choice a label is displayed showing the bill.
I cant figure out how to add the cost when the check box is selected and remove the cost when it is de selected using the arrays.
Any help appreciated.
This is my code so far:
package Lab4EventHandling;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Frame3 extends JFrame implements ActionListener {
private Container cPane;
private JLabel tMsg, bMsg;
private JCheckBox americano, espresso, doubleEspresso, latte;
JCheckBox[] boxes = new JCheckBox[]{americano, espresso, doubleEspresso, latte};
private JPanel checkPanel = new JPanel(new GridLayout(0,1));
private Color cl;
private double cost = 0;
private final int WINDOW_WIDTH = 200;
private final int WINDOW_HEIGHT = 200;
private final int x = 550;
private final int y = 400;
public Frame3()
{
cPane = getContentPane();
cl = new Color(150, 150, 250);
cPane.setBackground(cl);
this.setLayout(new BorderLayout(0,1));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(x,y);
this.add(checkPanel, BorderLayout.CENTER);
tMsg = new JLabel("Choose a coffee:" ,SwingConstants.CENTER);
tMsg.setBorder(BorderFactory.createEmptyBorder(4,4,4,4));
this.add(tMsg, BorderLayout.PAGE_START);
americano = new JCheckBox("Americano", false);
checkPanel.add(americano);
americano.addActionListener(this);
espresso = new JCheckBox("Espresso", false);
checkPanel.add(espresso);
espresso.addActionListener(this);
doubleEspresso = new JCheckBox("Double Espresso", false);
checkPanel.add(doubleEspresso);
doubleEspresso.addActionListener(this);
latte = new JCheckBox("Latte", false);
checkPanel.add(latte);
latte.addActionListener(this);
bMsg = new JLabel("Bill is ");
bMsg.setHorizontalAlignment(SwingConstants.CENTER);
bMsg.setBorder(BorderFactory.createEmptyBorder(4,4,4,4));
this.add(bMsg, BorderLayout.SOUTH);
this.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
Double[] array = {3.75, 4.00, 4.50, 3.50};
for (JCheckBox box : boxes) {
if(americano.isSelected())
{
cost += 3.75;
String r = String.valueOf(cost);
bMsg.setText(r);
}
if(espresso.isSelected())
{
cost += 4.00;
String r = String.valueOf(cost);
bMsg.setText(r);
}
else if(doubleEspresso.isSelected())
{
cost = 4.50;
String r = String.valueOf(cost);
bMsg.setText(r);
}
else if(latte.isSelected())
{
cost = 3.50;
String r = String.valueOf(cost);
bMsg.setText(r);
}
}
}
public class Frame3Test{
public static void main(String [] args)
{
Frame3 f = new Frame3();
f.setVisible(true);
}
}
Your first problem (which you may or may not have noticed) is your array of JCheckBoxes only contains null elements:
// check boxes are null here
private JCheckBox americano, espresso, doubleEspresso, latte;
// array created with only null elements
JCheckBox[] boxes = new JCheckBox[]{americano, espresso, doubleEspresso, latte};
So you need to create the array after instantiating the actual check boxes.
...
americano = new JCheckBox("Americano", false);
checkPanel.add(americano);
americano.addActionListener(this);
espresso = new JCheckBox("Espresso", false);
checkPanel.add(espresso);
espresso.addActionListener(this);
doubleEspresso = new JCheckBox("Double Espresso", false);
checkPanel.add(doubleEspresso);
doubleEspresso.addActionListener(this);
latte = new JCheckBox("Latte", false);
checkPanel.add(latte);
boxes = new JCheckBox[] {
americano, espresso, doubleEspresso, latte
};
Then the assignment is suggesting to use arrays because you can create another parallel array of prices (which you did). But since you need these prices in parallel you cannot use a for each loop. You need the index. Then you need to recalculate the entire cost every time anything is selected or deselected.
final double[] prices = {
3.75, 4.00, 4.50, 3.50
};
...
double total = 0.0;
for(int i = 0; i < boxes.length; i++) {
if(boxes[i].isSelected()) {
total += prices[i];
}
}
There's two other notes that seem to be outside the scope of the assignment:
You should always make a class for this kind of association.
You should never use double for money. Use BigDecimal or something like it.
Using a class makes logic simpler and not using double makes the calculation not incur error for this decimal addition.
class PricePair {
JCheckBox jCheckBox;
BigDecimal price;
}
BigDecimal total = new BigDecimal("0.00");
for(PricePair option : options) {
if(option.jCheckBox.isSelected()) {
total = total.add(option.price);
}
}
First of all, you're not handling all the checkboxes the same way. For the first two choices, you add the price to the cost:
cost += 3.75;
whereas for the last two choices, you replace the cost:
cost = 4.50;
The first way is the correct way.
Second, there's no reason to have the cost as a field. You should recompute the cost, and it should always start with 0, every time a checkbox selection changes. So cost should be a local variable of the actionPerformed() method.
Third, there's no reason to change the label value before you know the final cost. So the lines
String r = String.valueOf(cost);
bMsg.setText(r);
should only be there once, at the end of the actionPerformed() method, when the final cost is known.
Finally, you want to use it us an array instead of handling each checkbos separately. It's quite easy, you just need to loop over the checkboxes:
double prices = {3.75, 4.00, 4.50, 3.50};
double cost = 0.0;
for (int i = 0; i < boxes.length; i++) {
if (boxes[i].isSelected()) {
double price = prices[i];
cost += price;
}
}
// now display the cost in the label
Although both answers posted here are very helpful I'm adding this one because IMHO arrays are the less Object Oriented thing ever. You can achieve a more robust and OO solution following this hints:
Use
putClientProperty()
method inherited from
JComponent
to hold the prices in the check boxes.
Implement an ActionListener to add/substract the check box price to the total depending on the check box state. Use getClientProperty() method to retrieve the value stored in the check box.
See the example below:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Demo {
BigDecimal total = new BigDecimal(BigInteger.ZERO);
private void createAndShowGUI() {
final JLabel totalLabel = new JLabel("Total: ");
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JCheckBox checkBox = (JCheckBox)e.getSource();
BigDecimal value = (BigDecimal)checkBox.getClientProperty("price");
total = checkBox.isSelected() ? total.add(value) : total.subtract(value);
StringBuilder sb = new StringBuilder("Total: ").append(total);
totalLabel.setText(sb.toString());
}
};
JCheckBox americano = new JCheckBox("Americano");
americano.addActionListener(actionListener);
americano.putClientProperty("price", new BigDecimal("3.75"));
JCheckBox espresso = new JCheckBox("Espresso");
espresso.addActionListener(actionListener);
espresso.putClientProperty("price", new BigDecimal("4.00"));
JCheckBox doubleEspresso = new JCheckBox("Double Espresso");
doubleEspresso.addActionListener(actionListener);
doubleEspresso.putClientProperty("price", new BigDecimal("4.50"));
JCheckBox latte = new JCheckBox("Latte");
latte.addActionListener(actionListener);
latte.putClientProperty("price", new BigDecimal("3.50"));
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
content.add(americano);
content.add(espresso);
content.add(doubleEspresso);
content.add(latte);
content.add(totalLabel);
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
This way you can forget about mapping every check box with a value using arrays or maps. If you need to add a new sort of coffee you should simply add 4 lines like this:
JCheckBox newCoffee = new JCheckBox("New Coffee");
newCoffee.addActionListener(actionListener);
newCoffee.putClientProperty("price", new BigDecimal("4.00"));
content.add(newCoffee);

Categories