Domanda

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

    }
}
È stato utile?

Soluzione

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

Altri suggerimenti

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:

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top