Change JLabel based on what user choose - java
I am task to do a simple order system. I want the JLabel (Amount: $0.00) show the corresponding amount on what the user picks for his burger and condiments. For example, if the user click on beef, the label will change into "Amount: $4.00", and when he choose a condiment, it will add $0.50 to the total based on how many condiments he picks and vice versa. Also, when the user unchecks a condiment (JCheckBox), it will deduct $0.50 dollars to the total.
My code for beef JRadioButton:
private void beef_radioBtnActionPerformed(java.awt.event.ActionEvent evt) {
total_amount.setText("Amount: $4.00");
ketchup_Checkbox.setEnabled(true);
mustard_Checkbox.setEnabled(true);
pickles_Checkbox.setEnabled(true);
}
Code for ketchup JCheckBox:
private void ketchup_CheckboxActionPerformed(java.awt.event.ActionEvent evt) {
float condiments_amount = (float) 0.50;
float beef_amount = (float) 4.00;
float total;
if (beef_radioBtn.isSelected()){
total = beef_amount + condiments_amount;
total_amount.setText("Amount: $" + decimal.format(total));
if (!ketchup_Checkbox.isSelected()){
total_amount.setText("Amount: $" + decimal.format(4.50 - condiments_amount));
}
else if (mustard_Checkbox.isSelected()){
total_amount.setText("Amount: $" + decimal.format(4.50 + condiments_amount));
}
else if (pickles_Checkbox.isSelected()){
total_amount.setText("Amount: $" + decimal.format(4.50 + condiments_amount));
}
}
}
Okay, buckle up, this is going to be a bit of ride.
One of the most powerful concepts you have available to you is the concept of "model". A model is just something that "models" something and is a way to seperate different areas of your program. So a model, models the data (think of it like a container) and the view will then use those models to format the data to the user (separation of concerns). A model may also contain business logic or perform calculations depending on its requirements.
The allows you to centralise concepts so you don't end up repeating yourself or forgetting to do things. It's also a way to change how parts of the program work, also known as "delegation"
Starting point, some interfaces
Well, that's a lot of "blah", so let's get started. I prefer to use interfaces to describe things, it provides a lot of freedom, as you can put different interfaces together to suit different requirements.
The Menu
Okay, simple concept, this will be a list of items which are available to sell
public interface Menu {
public List<MainMenuItem> getMenuItems();
}
Menu items
A description of a menu item, pretty basic
public interface MenuItem {
public String getDescription();
public double getPrice();
}
"Main" menu items
These are all the "top level", "stand alone" menu items and in our case, can have condiments :D
public interface MainMenuItem extends MenuItem {
public List<Condiment> getCondiments();
}
Condiments
Condiments are a "special" MenuItem, as they are associated with a MainMenuItem
public interface Condiment extends MenuItem {
}
Burgers
This is just a demonstration of some of the things you could do, Burger isn't anything special, but as you will see, we can use this concept to do different things
public interface Burger extends MainMenuItem {
}
Order
And finally, the "order", what have we ordered and what condiments do we want with it
public interface Order {
public MainMenuItem getItem();
public void setItem(MainMenuItem item);
public List<Condiment> getCondiments();
public void addCondiment(Condiment condiment);
public void removeCondiment(Condiment condiment);
public double getTally();
}
This is a good demonstration of the power of the model. The Order has a getTally method, which is used to calculate what is owed. Different implementations of the model might apply different calculations, like tax or discounts
Implementations
Okay, since you're probably aware, we can't create an instance of a interface, we need some "default" implementations to work with...
public class DefaultOrder implements Order {
private MainMenuItem item;
private List<Condiment> condiments = new ArrayList<>();
#Override
public MainMenuItem getItem() {
return item;
}
#Override
public List<Condiment> getCondiments() {
return Collections.unmodifiableList(condiments);
}
#Override
public double getTally() {
double tally = 0;
if (item != null) {
tally += item.getPrice();
}
for (Condiment condiment : condiments) {
tally += condiment.getPrice();
}
return tally;
}
#Override
public void setItem(MainMenuItem item) {
this.item = item;
// Oh look, we've established a "rule" that this model
// applies, by itself, sweet
condiments.clear();
}
#Override
public void addCondiment(Condiment condiment) {
// Bit pointless if the menu item is not set
if (item == null) {
return;
}
// Probably should check for duplicates
condiments.add(condiment);
}
#Override
public void removeCondiment(Condiment condiment) {
// Bit pointless if the menu item is not set
if (item == null) {
return;
}
condiments.remove(condiment);
}
}
public class DefaultMenu implements Menu {
private List<MainMenuItem> menuItems = new ArrayList<>();
public void add(MainMenuItem menuItem) {
menuItems.add(menuItem);
}
#Override
public List<MainMenuItem> getMenuItems() {
return Collections.unmodifiableList(menuItems);
}
}
public abstract class AbstractMenuItem implements MenuItem {
private String description;
private double price;
public AbstractMenuItem(String description, double price) {
this.description = description;
this.price = price;
}
#Override
public String getDescription() {
return description;
}
#Override
public double getPrice() {
return price;
}
}
public class DefaultCondiment extends AbstractMenuItem implements Condiment {
public DefaultCondiment(String description, double price) {
super(description, price);
}
}
public class DefaultBurger extends AbstractMenuItem implements Burger {
private List<Condiment> condiments;
public DefaultBurger(String description, double price, List<Condiment> condiments) {
super(description, price);
// Protect ourselves from external modifications
this.condiments = new ArrayList<>(condiments);
}
#Override
public List<Condiment> getCondiments() {
return Collections.unmodifiableList(condiments);
}
}
Okay, try not to get too caught up in this, but have a look at the use of abstract here. AbstractMenuItem encapsulates a lot of the "common" functionality that all MenuItem implementations are going to need, so we don't need to repeat ourselves, sweet.
Some of these implementations are already making decisions or applying rules. For example, the DefaultOrder will clear the condiments when ever the MainMenuItem is changed. It could also make sure that the condiment which is been applied is actually available fo this item.
Also note, the tally method is not a stored property, but is re-calculated every time you call it. This is design decision, it wouldn't be hard to make it a stored property instead, so each time you change the MenuMenuItem, add and/or remove condiments, the property was updated, but I'm feeling lazy. But you can see how these things can be changed, and it will effect ALL users of these models, sweet :D
Okay, but how does this actually answer the question? Well, quite a bit actually.
So, the idea is, you start with a blank Order. The user selects a "main item" (ie a burger), you set this to the Order and then you update the UI in response. The UI asks the Order to calculate the tally and presents that to the user.
More over, the same concept works for condiments as well. Each time a condiment is added or removed by the user, the Order is updated and you update the UI.
Okay, but maybe, it's a little easier to understand with an example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
List<Condiment> condiments = new ArrayList<>(3);
condiments.add(new DefaultCondiment("Ketchup", 0.5));
condiments.add(new DefaultCondiment("Mustard", 0.5));
condiments.add(new DefaultCondiment("Pickles", 0.5));
DefaultMenu menu = new DefaultMenu();
menu.add(new DefaultBurger("Beef", 4.0, condiments));
menu.add(new DefaultBurger("Chicken", 3.5, condiments));
menu.add(new DefaultBurger("Veggie", 4.0, condiments));
MenuPane menuPane = new MenuPane();
menuPane.setMenu(menu);
frame.add(menuPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
private Menu menu;
private Order order;
private List<Condiment> selectedCondiments = new ArrayList<>();
private JPanel burgerPanel;
private JPanel condimentPanel;
private JPanel totalPanel;
private JLabel totalLabel;
private JButton clearButton;
private JButton payButton;
private NumberFormat currencyFormatter;
public MenuPane() {
setLayout(new GridBagLayout());
order = new DefaultOrder();
burgerPanel = new JPanel();
burgerPanel.setBorder(new EmptyBorder(8, 8, 8, 8));
condimentPanel = new JPanel();
condimentPanel.setBorder(new EmptyBorder(8, 8, 8, 8));
totalPanel = makeTotalPanel();
totalPanel.setBorder(new EmptyBorder(8, 8, 8, 8));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 0.5;
add(burgerPanel, gbc);
gbc.gridy++;
add(condimentPanel, gbc);
gbc.gridy++;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(totalPanel, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected NumberFormat getCurrentFormatter() {
if (currencyFormatter != null) {
return currencyFormatter;
}
currencyFormatter = NumberFormat.getCurrencyInstance();
currencyFormatter.setMinimumFractionDigits(2);
return currencyFormatter;
}
protected JPanel makeTotalPanel() {
JPanel totalPanel = new JPanel(new GridBagLayout());
totalLabel = new JLabel();
clearButton = new JButton("CLR");
payButton = new JButton("PAY");
clearButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
order = new DefaultOrder();
buildCondiments();
orderDidChange();
}
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 0;
totalPanel.add(totalLabel, gbc);
gbc.weightx = 0;
gbc.gridx++;
totalPanel.add(clearButton, gbc);
gbc.gridx++;
totalPanel.add(payButton, gbc);
return totalPanel;
}
protected void buildBurgerMenu() {
burgerPanel.removeAll();
burgerPanel.setLayout(new GridBagLayout());
if (menu == null) {
return;
}
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
ButtonGroup bg = new ButtonGroup();
// Stick with me, this is a little more advanced, but provides
// a really nice concept and ease of use
// We could also make use of the Action API, but that might
// pushing you just a little to far ;)
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JComponent)) {
return;
}
JComponent comp = (JComponent) e.getSource();
Object obj = comp.getClientProperty("MenuItem");
// I'm putting this here to demonstrate part of the concept
// of polymorphism - techncially, we don't have to care
// of it's a burger or some other type of menu item,
// only that this is going to represent the "main" item
if (!(obj instanceof MainMenuItem)) {
return;
}
MainMenuItem item = (MainMenuItem) obj;
order.setItem(item);
buildCondiments();
orderDidChange();
}
};
System.out.println(menu.getMenuItems().size());
for (MenuItem item : menu.getMenuItems()) {
// Only interested in burgers
// Could have the Menu do this, but that's a design
// decision
if (!(item instanceof Burger)) {
continue;
}
Burger burger = (Burger) item;
JRadioButton btn = new JRadioButton(burger.getDescription() + " (" + getCurrentFormatter().format(burger.getPrice()) + ")");
// Ok, this is just a little cheeky, but we're associating the
// butger with the button for simplicity
btn.putClientProperty("MenuItem", burger);
bg.add(btn);
// Add all the buttons share the same listener, because of polymorphism :D
btn.addActionListener(actionListener);
burgerPanel.add(btn, gbc);
}
}
protected void buildCondiments() {
condimentPanel.removeAll();
condimentPanel.setLayout(new GridBagLayout());
if (menu == null || order.getItem() == null) {
return;
}
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
// Stick with me, this is a little more advanced, but provides
// a really nice concept and ease of use
// We could also make use of the Action API, but that might
// pushing you just a little to far ;)
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JCheckBox)) {
return;
}
JCheckBox checkBox = (JCheckBox) e.getSource();
Object obj = checkBox.getClientProperty("Condiment");
if (!(obj instanceof Condiment)) {
return;
}
Condiment condiment = (Condiment) obj;
if (checkBox.isSelected()) {
order.addCondiment(condiment);
} else {
order.removeCondiment(condiment);
}
orderDidChange();
}
};
for (Condiment condiment : order.getItem().getCondiments()) {
JCheckBox btn = new JCheckBox(condiment.getDescription() + " (" + getCurrentFormatter().format(condiment.getPrice()) + ")");
// Ok, this is just a little cheeky, but we're associating the
// butger with the button for simplicity
btn.putClientProperty("Condiment", condiment);
// Add all the buttons share the same listener, because of polymorphism :D
btn.addActionListener(actionListener);
condimentPanel.add(btn, gbc);
}
}
public Menu getMenu() {
return menu;
}
public void setMenu(Menu menu) {
this.menu = menu;
order = new DefaultOrder();
buildBurgerMenu();
orderDidChange();
}
protected void orderDidChange() {
if (order == null) {
totalLabel.setText("Amount: " + getCurrentFormatter().format(0));
return;
}
// And now, some magic, how easy is it to get the expected
// tally amount!!
totalLabel.setText("Amount: " + getCurrentFormatter().format(order.getTally()));
}
}
public interface Menu {
public List<MainMenuItem> getMenuItems();
}
public interface MenuItem {
public String getDescription();
public double getPrice();
}
public interface Condiment extends MenuItem {
}
public interface MainMenuItem extends MenuItem {
public List<Condiment> getCondiments();
}
public interface Burger extends MainMenuItem {
}
public interface Order {
public MainMenuItem getItem();
public void setItem(MainMenuItem item);
public List<Condiment> getCondiments();
public void addCondiment(Condiment condiment);
public void removeCondiment(Condiment condiment);
public double getTally();
}
public class DefaultOrder implements Order {
private MainMenuItem item;
private List<Condiment> condiments = new ArrayList<>();
#Override
public MainMenuItem getItem() {
return item;
}
#Override
public List<Condiment> getCondiments() {
return Collections.unmodifiableList(condiments);
}
#Override
public double getTally() {
double tally = 0;
if (item != null) {
tally += item.getPrice();
}
for (Condiment condiment : condiments) {
tally += condiment.getPrice();
}
return tally;
}
#Override
public void setItem(MainMenuItem item) {
this.item = item;
// Oh look, we've established a "rule" that this model
// applies, by itself, sweet
condiments.clear();
}
#Override
public void addCondiment(Condiment condiment) {
// Bit pointless if the menu item is not set
if (item == null) {
return;
}
// Probably should check for duplicates
condiments.add(condiment);
}
#Override
public void removeCondiment(Condiment condiment) {
// Bit pointless if the menu item is not set
if (item == null) {
return;
}
condiments.remove(condiment);
}
}
public class DefaultMenu implements Menu {
private List<MainMenuItem> menuItems = new ArrayList<>();
public void add(MainMenuItem menuItem) {
menuItems.add(menuItem);
}
#Override
public List<MainMenuItem> getMenuItems() {
return Collections.unmodifiableList(menuItems);
}
}
public abstract class AbstractMenuItem implements MenuItem {
private String description;
private double price;
public AbstractMenuItem(String description, double price) {
this.description = description;
this.price = price;
}
#Override
public String getDescription() {
return description;
}
#Override
public double getPrice() {
return price;
}
}
public class DefaultCondiment extends AbstractMenuItem implements Condiment {
public DefaultCondiment(String description, double price) {
super(description, price);
}
}
public class DefaultBurger extends AbstractMenuItem implements Burger {
private List<Condiment> condiments;
public DefaultBurger(String description, double price, List<Condiment> condiments) {
super(description, price);
// Protect ourselves from external modifications
this.condiments = new ArrayList<>(condiments);
}
#Override
public List<Condiment> getCondiments() {
return Collections.unmodifiableList(condiments);
}
}
}
Okay, that's a lot to take in. Lets take a closer look at the buildBurgerMenu method. This gets called when ever the main menu is changed.
Pay close attention to the actionListener used in this method, there's only one and it's shared by all the buttons
protected void buildBurgerMenu() {
burgerPanel.removeAll();
burgerPanel.setLayout(new GridBagLayout());
if (menu == null) {
return;
}
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
ButtonGroup bg = new ButtonGroup();
// Stick with me, this is a little more advanced, but provides
// a really nice concept and ease of use
// We could also make use of the Action API, but that might
// pushing you just a little to far ;)
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JComponent)) {
return;
}
JComponent comp = (JComponent) e.getSource();
Object obj = comp.getClientProperty("MenuItem");
// I'm putting this here to demonstrate part of the concept
// of polymorphism - techncially, we don't have to care
// of it's a burger or some other type of menu item,
// only that this is going to represent the "main" item
if (!(obj instanceof MainMenuItem)) {
return;
}
MainMenuItem item = (MainMenuItem) obj;
order.setItem(item);
buildCondiments();
orderDidChange();
}
};
System.out.println(menu.getMenuItems().size());
for (MenuItem item : menu.getMenuItems()) {
// Only interested in burgers
// Could have the Menu do this, but that's a design
// decision
if (!(item instanceof Burger)) {
continue;
}
Burger burger = (Burger) item;
JRadioButton btn = new JRadioButton(burger.getDescription() + " (" + getCurrentFormatter().format(burger.getPrice()) + ")");
// Ok, this is just a little cheeky, but we're associating the
// butger with the button for simplicity
btn.putClientProperty("MenuItem", burger);
bg.add(btn);
// Add all the buttons share the same listener, because of polymorphism :D
btn.addActionListener(actionListener);
burgerPanel.add(btn, gbc);
}
}
When ever the actionListener is triggered (via a user interaction for example), it makes a bunch of decisions, which, if all goes well, ends in the Order been updated, the buildCondiments and orderDidChange methods been called, which updates the available condiments and updates the UI's tally.
This is all done in a "abstract" way. The actionListener doesn't care about what type of MainMenuItem the user selected, that doesn't change its workflow, it only needs to apply the item to the Order. In the same vain, the Order doesn't care, as it just needs the price information in order to calculate the tally.
So you can add new menu items and/or change the prices and everything just keeps on working (🤞).
Let's look at the orderDidChange method...
protected void orderDidChange() {
if (order == null) {
totalLabel.setText("Amount: " + getCurrentFormatter().format(0));
return;
}
// And now, some magic, how easy is it to get the expected
// tally amount!!
totalLabel.setText("Amount: " + getCurrentFormatter().format(order.getTally()));
}
Not super complicated is it! All the work is been done by the, MODEL!
What I left out
For brevity, I left out one other concept, often used with models, the concept of the "observer pattern".
The observer pattern allows an interested party to be notified when some other object changes. It's pretty common concept and you've already used, ActionListener is an example of an observer pattern. It allows you to "observer" "actions events" when they are triggered by a given object.
Sure, but how is that helpful?
Well, imagine now if, instead of having to manually call orderDidChange every time you wanted to update the UI (or even forgetting to and spending a few hours debugging why), the MenuPane could, instead, registered itself as an observer directly to the Order and be notified when the order changed!! Super sweet!
This further helps you de-couple your code and makes it super easy to update the UI in a verity of ways independently of the model or other code requirements.
Models 💪
Related
How to pass a variable between JPanel classes?
I have a JTabbedPane with two JPanels that need to stay in seperate classes. In PageOne, I want to be able to increment MyInteger by clicking the add button, and I then want to be able to print that integer in PageTwo by clicking the button there. It prints the correct value in PageOne, but prints 0 when I pass it to the PageTwo class and print it there. How can I pass the value in such a way that it prints the correct value when clicking the button in both JPanels? I figure it has something to do with how I inherit from PageOne, but couldn't find a way of changing it on SO that solved my problem. Main class: import javax.swing.*; public class MyJFrame { PageOne pageOne; PageTwo pageTwo; public MyJFrame() { JFrame f = new JFrame(); pageOne = new PageOne(); pageTwo = new PageTwo(); JTabbedPane jTabbedPane = new JTabbedPane(); jTabbedPane.addTab("Page One", pageOne); jTabbedPane.addTab("Page Two", pageTwo); f.add(jTabbedPane); f.setSize(200,120); f.setVisible(true); } public static void main(String[] args) throws InterruptedException { new MyJFrame(); } } JPanel One: import javax.swing.*; public class PageOne extends JPanel { public Integer myInteger = 0; public JButton add; public PageOne() { add = new JButton(); add.setText("Increment number"); add(add); add.addActionListener(actionEvent -> { myInteger++; printOne(); }); } public void printOne() { System.out.println("Page One:" + myInteger); } } JPanel Two: import javax.swing.*; public class PageTwo extends JPanel { PageOne pageOneRef = new PageOne(); public JButton button; public PageTwo() { JPanel panel = new JPanel(); button = new JButton("Click me"); panel.add(button); add(panel); button.addActionListener(e -> printTwo()); } public void printTwo() { System.out.println("Page Two:" + pageOneRef.myInteger); } }
The basic answer is, you need some kind of "container" which can be shared between the two components. This is commonly achieved through the use of a "model" of some kind. See: Model-View-Controller Observer Pattern Writing Event Listeners for an overview of the concepts presented below Runnable example import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.ArrayList; import java.util.List; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; public class Main { public static void main(String[] args) { new Main(); } public Main() { EventQueue.invokeLater(new Runnable() { #Override public void run() { JFrame frame = new JFrame(); DefaultIntegerModel model = new DefaultIntegerModel(); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab("Page One", new PageOne(model)); tabbedPane.addTab("Page Two", new PageTwo(model)); frame.add(tabbedPane); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public interface IntegerModel { public interface Observer { public void valueDidChange(IntegerModel source, int value); } public int getValue(); public void addObserver(Observer observer); public void removeObserver(Observer observer); } public interface MutableIntegerModel extends IntegerModel { public void setValue(int value); } public class DefaultIntegerModel implements MutableIntegerModel { private int value; private List<Observer> observers; public DefaultIntegerModel() { this(0); } public DefaultIntegerModel(int value) { this.value = value; observers = new ArrayList<Observer>(8); } #Override public void setValue(int value) { this.value = value; fireValueDidChange(value); } #Override public int getValue() { return value; } #Override public void addObserver(Observer observer) { observers.add(observer); } #Override public void removeObserver(Observer observer) { observers.remove(observer); } protected void fireValueDidChange(int value) { for (Observer observer : observers) { observer.valueDidChange(this, value); } } } public class PageOne extends JPanel { public JButton add; private MutableIntegerModel model; public PageOne(MutableIntegerModel model) { this.model = model; add = new JButton(); add.setText("Increment number"); add(add); add.addActionListener(actionEvent -> { model.setValue(model.getValue() + 1); printOne(); }); } public void printOne() { System.out.println("Page One:" + model.getValue()); } } public class PageTwo extends JPanel { private JButton button; private JLabel label; private IntegerModel model; public PageTwo(IntegerModel model) { this.model = model; model.addObserver(new IntegerModel.Observer() { #Override public void valueDidChange(IntegerModel source, int value) { System.out.println("Page two value did change to " + value); label.setText(Integer.toString(model.getValue())); } }); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; label = new JLabel(Integer.toString(model.getValue())); add(label, gbc); button = new JButton("Click me"); button.addActionListener(e -> printTwo()); add(button, gbc); } public void printTwo() { System.out.println("Page Two:" + model.getValue()); } } } But why are there two models Stop for a second and think about the responsibilities of each component. PageOne want's to update the model, in order to do so, it also needs to know the value of the model. The model makes no assumption about "how" the consumer of this model will do that (so I didn't provide a increment method), it just allows the consumer to set the value it wants PageTwo just wants to display the value (and be notified when some change occurs), so it doesn't need a mutable version of the model. This restricts what consumers maybe able to do to the model rather the exposing functionality to parties which don't need it (and might be tempted to abuse it) This is a demonstration and your needs may differ, but I'm bit of a scrooge when I design these kinds of things, I need the consumers to prove to me that they need functionality, rather then "assuming" what functionality they "might" require 😉 This is a practice known is "information hiding", which is supported by Polymorphism in OO languages
How do I make my two classes function together?
I am working on a text-based videogame and created a GUI for it, however, I am having issues getting my two classes to work together. I have a text field in my GUI class that receives input from the user, which I want to send to my Player class so that it can compare it and execute the appropriate methods. I am still very new to programming, so I hope that some of you out there might be able to help me. Excuse the horrendous code. import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class GUI extends Player { String input; JLabel message; public GUI() { JFrame frame = new JFrame("SPACE GAME"); ImageIcon image = new ImageIcon("rocket.png"); frame.setIconImage(image.getImage()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(1200, 800); frame.setLocationRelativeTo(null); frame.setFocusable(true); frame.getContentPane(); JPanel panel = new JPanel(); JTextField commandLine = new JTextField(30); JLabel message = new JLabel(); frame.add(panel); panel.setBackground(Color.black); panel.setLayout(null); panel.setBorder(BorderFactory.createEmptyBorder(1000, 1000 ,1000 ,1000)); commandLine.setBackground(Color.WHITE); commandLine.setBounds(5, 730, 300, 30); commandLine.setBorder(BorderFactory.createLineBorder(Color.GRAY, 3)); commandLine.setFont(new Font("Zig", Font.PLAIN, 18)); commandLine.setForeground(Color.GREEN); commandLine.setBackground(Color.BLACK); commandLine.setCaretColor(Color.GREEN); commandLine.addActionListener(new ActionListener() { #Override public void actionPerformed(ActionEvent e) { String input = commandLine.getText(); } }); commandLine.setVisible(false); message.setForeground(Color.GREEN); message.setText("Welcome to SPACE GAME! Press any key to start."); message.setBounds(5,665, 1000, 100); message.setFont(new Font("Courier", Font.PLAIN, 18)); panel.add(message); panel.add(commandLine); frame.setVisible(true); frame.addKeyListener(new KeyListener() { #Override public void keyTyped(KeyEvent e) { } #Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); commandLine.setVisible(true); commandLine.requestFocusInWindow(); message.setText("Type \"help\" for help"); } #Override public void keyReleased(KeyEvent e) { } }); } public JLabel getMessage(JLabel message) { return message; } } import javax.swing.*; import java.util.Scanner; public class Player { //Attributes private Room currentRoom; Player player; JLabel message; // Handles player movement and commands public void move() { player = new Player(); Map map = new Map(); GUI gui = new GUI(); player.currentRoom = map.room1; Scanner input = new Scanner(System.in); System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()); System.out.println("Type \"help\" to get help"); //Commands boolean isGameRunning = true; while (isGameRunning) { String goMessage = input.nextLine(); goMessage = goMessage.toLowerCase(); switch (goMessage) { case "go north", "north", "go n", "n": goNorth(); break; case "go east", "east", "go e", "e": goEast(); break; case "go south", "south", "go s", "s": goSouth(); break; case "go west", "west", "go w", "w": goWest(); break; case "exit": isGameRunning = false; break; case "look": System.out.println(player.currentRoom.getRoomDescription()); break; case "help": System.out.println(""" "go (north, south, east, west)" to choose a direction to go. "look" gives you a description of the room. "exit" stops the game."""); break; default: System.out.println("Unknown command"); break; } } } public void goNorth() { if (player.currentRoom.getNorthRoom() != null) { player.currentRoom = player.currentRoom.getNorthRoom(); System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()); } else { System.out.println("You cannot go that way"); } } public void goEast() { if (player.currentRoom.getEastRoom() != null) { player.currentRoom = player.currentRoom.getEastRoom(); System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()); } else { System.out.println("You cannot go that way"); } } public void goSouth() { if (player.currentRoom.getSouthRoom() != null) { player.currentRoom = player.currentRoom.getSouthRoom(); System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()); } else { System.out.println("You cannot go that way"); } } public void goWest() { if (player.currentRoom.getWestRoom() != null) { player.currentRoom = player.currentRoom.getWestRoom(); System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()); } else { System.out.println("You cannot go that way"); } } }
There are some good things in your code as well as several major issues that may hamper your ability to mash a GUI with your current console-program code, and these include: You do have some separation of the your program logic into separate classes such as Player, Map, and Room classes, and that is a very good thing, but you still could do more separation of the logic, and doing so can simplify your code making it easier to debug and enhance. The Player class contains a Player instance, I'm not sure why. The outer main class should probably be re-named from Player to the Game class, and the Player class should be a totally separate class, one that holds the state of a single player instance, and shouldn't create instances of itself as you're doing. The key is to strive to "oop-ify" your code, especially the "model" portion of the code, the code that holds the non-user interface program logic, to make it more object-oriented, since this will make it much easier to allow you to connect a GUI or any other interface, to your model. Your model (again, named Player) has user interface (UI) code within it: it has Scanner input code within it and println statements. This is problematic, especially now since you want to enhance the program and make it work with a GUI. The key thing that you must do, first and foremost, is to separate the user interface code (agian the code that gets input from the user and that displays output to the user) outside of model class, which is, again, class that holds the program logic. Your Player class has a lot of non-Player code cluttering it. A basic OOPs principle is that a class should have a single responsibility (called the "single responsibility rule"), and so your Player class should only have code that is needed to encapsulate Player state (such as name, Room, perhaps other properties such as strength, weapons, health, intelligence....) and Player behavior (methods that allow Player to interact with other classes), and nothihg more or less. Going on the same vein, it appears that all the program data and behaviors seem to be hard-wired into this single main Player class that drives the program. This is very limiting and will hinder your ability to create a rich playing environment with many rooms, items in the rooms, and things like this. Divide and conquer -- get that information out of the main program and put it into OOP-compliant classes, including a Room class that has fields for items it might contain, that has knowledge of its own connections to other rooms. Your GUI program extends the Player object. This is a major problem since this program structure does not pass the basic test of inheritance, the "is-a" test: logically is a GUI a more specific type of player (much like a Dog is a more specific type of Animal, a structure which passes the "is-a" test)? No, it is not, and while this distinction may seem pendantic, you may try to use Player fields within the GUI class because of this inheritance, but if you try to do this, the code will not work correctly. Remove this inheritance, and instead try to connect classes through "composition" where one class holds an instance of another, rather than extends another. So for instance, the GUI could possibly hold a Player variable rather than extend from Player. Bottom line: You will want to fully "OOP-ify" your code before trying to add a GUI In order for the program to be able to well-meshed with a GUI, I would suggest making your underlying logical code (the non-user interface logic code) much more object-oriented with classes that all have a single responsibility, classes that are easily testable in isolation (away from GUI or any user interface). Start with first principles and the program will be much easier to build. So, for example, classes that could be considered for an adventure game include: A Direction class, or better a Direction enum. This would encapsulate the idea of "direction" and would be used for objects in the game to know which direction they are going and to be able to communicate this to other objects using constants, rather than Strings. The user of an enum would allow the compiler to check that directions are being used correctly, since if you used Strings, say, "West", the compiler would not automatically know if you mis-typed the String and used "Best" instead. Something simple like this, for example: public enum Direction { NORTH, EAST, SOUTH, WEST } A GameRoom class This would hold information telling it its location in the grid of rooms, Strings for both name and description properties, and a game player field or a List of game players if more than one are allowed, perhaps something that contains code like this:.... public class GameRoom { // map with connections to other game rooms private Map<Direction, GameRoom> connections = new HashMap<Direction, GameRoom>(); // location information private int x; private int y; // identifying information private String roomName; private String description; // holds any player objects that may be in the room private List<GamePlayer> playersInRoom = new ArrayList<>(); The class will have appropriate constructors as well as getter and setter methods and also: An addPlayer(...) method that allows the game to add a player into the room: public void addPlayer(GamePlayer gamePlayer) { playersInRoom.add(gamePlayer); gamePlayer.setCurrentRoom(this); // we'll get to this later } A public boolean move(...) method that moves a player contained by the room to a connecting room. It first checks that the room actually contains the player being moved, and then next checks if the room has a connection to another room in the requested direction. If either are false, then the method returns false to let the calling code that the attempted move failed. Otherwise, if the move is allowed, it returns true: public boolean move(GamePlayer gamePlayer, Direction direction) { // if the room doesn't currently hold this player if (!playersInRoom.contains(gamePlayer)) { return false; // invalid move request } // if the room doesn't have a connecting room in the requested direction if (!connections.containsKey(direction)) { return false; // invalid move request } // otherwise, we're good playersInRoom.remove(gamePlayer); connections.get(direction).addPlayer(gamePlayer); return true; } A GamePlayer class This class will hold several properties such as a String for name and a GameRoom field for the current room, constructors, getter and setter methods. It's declaration and fields could look something like: public class GamePlayer { private String name; private GameRoom currentRoom; //.... other properties It too should have a move method that calls the currentRoom GameRoom's move method and returns the same boolean, a result that tells the calling code if the move was valid and successful: public boolean move(Direction direction) { return currentRoom.move(this, direction); } A GameModel class: This will hold the state of the current game, fields for players, a data structure to hold all the rooms. It can have its own move method..... Only after creating your OOP-compliant classes should you move to the next step: the creation of your GUI or "view" class/classes. This class could hold a GameModel instance, and would be responsible for 1) displaying the state of the game (a visual representation of the rooms and the items that they hold), and 2) getting input from the user, and passing that input to the game model for processing. I like to use an M-V-C program structure, which stands for "Model-View-Controller" where the program logic and GUI are kept as separate as possible, and there may be a controller class or classes that help tie the model and view together. Another principle that I try to follow is to keep the GUI as "dumb" as possible. By dumb, I mean that it should get input from the user, perhaps do the most basic of input verifications, and should display the state of the model, but that's it. Almost all the "brains" of the program should be held by the model itself and not the view. Proof-of-concept Example: An incomplete but running "proof-of-concept" example of what I am describing above is shown below. You should copy the program whole, paste it into your IDE into a single file named VideoGame.java, and then should be able to run it. It uses some concepts that you may not yet be familiar with, including the use of Key Bindings to get user input from the GUI and the use of PropertyChangeListeners and PropertyChangeSupport objects to allow clean communication between objects (to notify listeners if the state of one of the model object has changed). This program should respond to pressing of the arrow keys: import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.*; import javax.swing.event.SwingPropertyChangeSupport; public class VideoGame { private static final int[][] ROOM_GRID_KEY = { { 1, 0, 0, 2, 0, 0, 3, 4 }, { 5, 6, 7, 8, 9, 10, 11, 0 }, { 0, 12, 0, 13, 0, 0, 0, 0 }, { 0, 14, 0, 0, 0, 0, 15, 16 }, { 17, 18, 0, 19, 0, 0, 20, 0 }, { 21, 22, 23, 24, 25, 26, 27, 28 } }; public static void main(String[] args) { SwingUtilities.invokeLater(() -> { GameModel gameModel = new GameModel(ROOM_GRID_KEY); GameView gameView = new GameView(); new GameController(gameModel, gameView); JFrame frame = new JFrame("Video Game"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(gameView); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); }); } } class GameController { private GameModel gameModel; private GameView gameView; public GameController(GameModel gameModel, GameView gameView) { this.gameModel = gameModel; this.gameView = gameView; ModelListener modelListener = new ModelListener(); gameView.setModel(gameModel); gameModel.addPropertyChangeListener(modelListener); } private class ModelListener implements PropertyChangeListener { #Override public void propertyChange(PropertyChangeEvent evt) { gameView.modelChange(evt); } } } #SuppressWarnings("serial") class DisplayPanel extends JPanel { private JPanel[][] panelGrid; private int gridCellSize; private GameModel gameModel; private GameRoom[][] roomGrid; public DisplayPanel(int gridCellSize) { this.gridCellSize = gridCellSize; } public void setModel(GameModel gameModel) { this.gameModel = gameModel; this.roomGrid = gameModel.getRoomGrid(); int rows = roomGrid.length; int cols = roomGrid[0].length; setBackground(Color.BLACK); setLayout(new GridLayout(rows, cols, 1, 1)); setBorder(BorderFactory.createLineBorder(Color.BLACK)); panelGrid = new JPanel[rows][cols]; for (int r = 0; r < panelGrid.length; r++) { for (int c = 0; c < panelGrid[r].length; c++) { JPanel panel = new JPanel(new GridBagLayout()); panelGrid[r][c] = panel; panel.setPreferredSize(new Dimension(gridCellSize, gridCellSize)); if (roomGrid[r][c] == null) { panel.setBackground(Color.BLACK); } else { panel.setBackground(Color.PINK); if (roomGrid[r][c].getPlayersInRoom().size() > 0) { GamePlayer gamePlayer = roomGrid[r][c].getPlayersInRoom().get(0); String name = gamePlayer.getName(); JLabel label = new JLabel(name); panel.add(label); } } add(panel); } } // key bindings code addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), Direction.SOUTH); addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), Direction.NORTH); addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), Direction.WEST); addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), Direction.EAST); } private void addBindings(KeyStroke keyStroke, Direction direction) { int condition = WHEN_IN_FOCUSED_WINDOW; InputMap inputMap = getInputMap(condition); ActionMap actionMap = getActionMap(); Action action = new AbstractAction() { #Override public void actionPerformed(ActionEvent e) { gameModel.move(direction); } }; inputMap.put(keyStroke, keyStroke.toString()); actionMap.put(keyStroke.toString(), action); } public void modelChange(PropertyChangeEvent evt) { for (int r = 0; r < panelGrid.length; r++) { for (int c = 0; c < panelGrid[r].length; c++) { JPanel panel = panelGrid[r][c]; if (roomGrid[r][c] != null) { if (roomGrid[r][c].getPlayersInRoom().size() > 0) { GamePlayer gamePlayer = roomGrid[r][c].getPlayersInRoom().get(0); String name = gamePlayer.getName(); JLabel label = new JLabel(name); panel.add(label); } else { panel.removeAll(); } } } } revalidate(); repaint(); } public GameModel getGameModel() { return gameModel; } } class GameView extends JPanel { private static final int CELL_SIZE = 80; private DisplayPanel displayPanel = new DisplayPanel(CELL_SIZE); private GameModel gameModel; // private JTextField textField = new JTextField(); public GameView() { setLayout(new BorderLayout()); add(displayPanel); // add(textField, BorderLayout.PAGE_END); } public void setModel(GameModel gameModel) { this.gameModel = gameModel; displayPanel.setModel(gameModel); } public void modelChange(PropertyChangeEvent evt) { displayPanel.modelChange(evt); } public GameModel getGameModel() { return gameModel; } } class GameModel { public static final String GAME_MODEL = "game model"; private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this); private GameRoom[][] roomGrid; private GamePlayer player = new GamePlayer("Fred"); public GameModel(int[][] roomGridKey) { roomGrid = new GameRoom[roomGridKey.length][roomGridKey[0].length]; // fill room grid with rooms if 1 in grid key array, with null if 0 for (int y = 0; y < roomGridKey.length; y++) { for (int x = 0; x < roomGridKey[0].length; x++) { roomGrid[y][x] = roomGridKey[y][x] != 0 ? new GameRoom("Some Room", "Some Description", x, y) : null; } } // make room connections: for (int y = 0; y < roomGrid.length; y++) { for (int x = 0; x < roomGrid[0].length; x++) { GameRoom thisRoom = roomGrid[y][x]; // if no room present, don't if (thisRoom == null) { continue; } if (x > 0) { GameRoom otherGameRoom = roomGrid[y][x - 1]; if (otherGameRoom != null) { thisRoom.putConnection(Direction.WEST, otherGameRoom); } } if (x < roomGrid[0].length - 1) { GameRoom otherGameRoom = roomGrid[y][x + 1]; if (otherGameRoom != null) { thisRoom.putConnection(Direction.EAST, otherGameRoom); } } if (y > 0) { GameRoom otherGameRoom = roomGrid[y - 1][x]; if (otherGameRoom != null) { thisRoom.putConnection(Direction.NORTH, otherGameRoom); } } if (y < roomGrid.length - 1) { GameRoom otherGameRoom = roomGrid[y + 1][x]; if (otherGameRoom != null) { thisRoom.putConnection(Direction.SOUTH, otherGameRoom); } } } } // put player in top left room GameRoom currentRoom = roomGrid[0][0]; if (currentRoom == null) { // some big error occurred System.err.println("Current room at 0, 0 is null. Exiting"); System.exit(-1); } player.setCurrentRoom(currentRoom); currentRoom.addPlayer(player); player.addPropertyChangeListener(pce -> pcSupport.firePropertyChange(GAME_MODEL, null, player)); } public boolean move(Direction direction) { boolean success = player.move(direction); return success; } public GamePlayer getPlayer() { return player; } public GameRoom[][] getRoomGrid() { return roomGrid; } public void addPropertyChangeListener(PropertyChangeListener listener) { pcSupport.addPropertyChangeListener(GAME_MODEL, listener); } } class GamePlayer { public static final String GAME_PLAYER = "game player"; private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this); private String name; private GameRoom currentRoom; public GamePlayer(String name) { super(); this.name = name; } public String getName() { return name; } public boolean move(Direction direction) { boolean success = currentRoom.move(this, direction); return success; } public void setCurrentRoom(GameRoom currentRoom) { GameRoom oldValue = this.currentRoom; GameRoom newValue = currentRoom; this.currentRoom = currentRoom; pcSupport.firePropertyChange(GAME_PLAYER, oldValue, newValue); } public GameRoom getCurrentRoom() { return currentRoom; } #Override public String toString() { return "GamePlayer [name=" + name + ", currentRoom=" + currentRoom + "]"; } public void addPropertyChangeListener(PropertyChangeListener listener) { pcSupport.addPropertyChangeListener(GAME_PLAYER, listener); } } class GameRoom { // map with connections to other game rooms private Map<Direction, GameRoom> connections = new HashMap<Direction, GameRoom>(); // location information private int x; private int y; // identifying information private String roomName; private String description; // holds any player objects that may be in the room private List<GamePlayer> playersInRoom = new ArrayList<>(); public GameRoom(String roomName, String description, int x, int y) { this.roomName = roomName; this.description = description; this.x = x; this.y = y; } public boolean move(GamePlayer gamePlayer, Direction direction) { // if the room doesn't currently hold this player if (!playersInRoom.contains(gamePlayer)) { return false; // invalid move request } // if the room doesn't have a connecting room in the requested direction if (!connections.containsKey(direction)) { return false; // invalid move request } // otherwise, we're good playersInRoom.remove(gamePlayer); connections.get(direction).addPlayer(gamePlayer); return true; } public void addPlayer(GamePlayer gamePlayer) { playersInRoom.add(gamePlayer); gamePlayer.setCurrentRoom(this); } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public String getRoomName() { return roomName; } public String getDescription() { return description; } public List<GamePlayer> getPlayersInRoom() { return playersInRoom; } public Map<Direction, GameRoom> getConnections() { return connections; } public void putConnection(Direction direction, GameRoom otherGameRoom) { connections.put(direction, otherGameRoom); } #Override public String toString() { return "GameRoom [x=" + x + ", y=" + y + "]"; } } enum Direction { NORTH, EAST, SOUTH, WEST } Again, compile and run this code and use the arrow keys to move "Fred" around the grid. A more complete program would separate the data completely out of the code and allow for file I/O for reading in the room grid into the program, possibly by creating a CSV file or files to hold the room information, or if the data is expected to grow and be more complex, then a relational database such as one that uses one of the flavors of SQL.
Since you've made the GUI extend the player, all you need to do is call the desired function from the ActionListener on the input field. commandLine.addActionListener(new ActionListener() { #Override public void actionPerformed(ActionEvent e) { String input = commandLine.getText(); if (input == "go north") { GoNorth(); } // ..... } }); However, things are pretty messy. There's no real reason for the GUI to extend the player, for starters. I'd advise you to separate the GUI class from Player. That way, you won't need to use the Scanner or Print, and instead collect all your GUI functionality in the GUI class. For instance: public void goNorth() { if (player.currentRoom.getNorthRoom() != null) { player.currentRoom = player.currentRoom.getNorthRoom(); gui.print("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription()) } else { gui.print("You cannot go that way"); } } You would need to implement a print function in the GUI class, that added a line of text to the message box, but I'm sure you were planning on that to begin with. In the GUI class, you would then have a reference to the player, so reading the input field would look something like this: commandLine.addActionListener(new ActionListener() { #Override public void actionPerformed(ActionEvent e) { String input = commandLine.getText(); switch(input) { case "go north": player.goNorth(); break; ... } } });
How to change the icon of a dynamically generated JButton
I have this java swing program, and im trying to figure out how can i create a button that upon clicking it will clear the text areas & change the icon of the person to put their hand down. The buttons are dynamically generated using a for loop And this // To create buttons for(int i=0 ; i < list.length; i++){ Participant pa = list[i]; JButton b = new JButton(pa.getNameButton(),participant); b.addActionListener(e -> { String s = pa.toString() + questionPane.getText(); final ImageIcon raise = resizeIcon(new ImageIcon("src/raise.png"),30,30); b.setIcon(raise); JOptionPane.showMessageDialog(null,s,"Welcome to Chat Room",JOptionPane.INFORMATION_MESSAGE,pa.getImage()); }); p.add(b); } // Clear button logic clearButton.addActionListener(e ->{ questionPane.setText(""); hostPane.setText(""); });
Okay, this is going to be a bit of fun. The following example decouples much of the concept and makes use of a basic "observer pattern" to notify interested parties that the state has changed (ie, the chat's been cleared). This is a basic concept where by you decouple the "what" from the "how", ie, "what" it is you want done (update the model) from the "how" it gets done (ie, button push). This makes it easier to adapt to more complex systems. The example contains a ChatService, which has a single listener, which, for this example, simple tells interested parties that the chat has been cleared. A more complex solution might have the ChatService generating events for when a user "raises" their hand, which allows the interested parties to deal with it in what ever way is relevant to them. The example makes use of the Action API to decouple the work performed by each action from the UI itself. This helps create a single unit of work which is easier to deal with when you have a dynamic data set. import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class Test { public static void main(String[] args) { new Test(); } public Test() { SwingUtilities.invokeLater(new Runnable() { #Override public void run() { JFrame frame = new JFrame(); frame.add(new TestPane()); frame.pack(); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { ChatService chatService = new ChatService(); JPanel panel = new JPanel(); String[] names = new String[] {"Bryan", "Alan", "George", "Henry"}; List<PeopleAction> actions = new ArrayList<>(names.length); for (String name : names) { PeopleAction action = new PeopleAction(chatService, name, false); actions.add(action); } Random rnd = new Random(); actions.get(rnd.nextInt(names.length)).setRaised(true); for (Action action : actions) { JButton btn = new JButton(action); panel.add(btn); } setLayout(new GridLayout(2, 1)); add(panel); JPanel hostPane = new JPanel(); JButton clearButton = new JButton(new ClearAction(chatService)); hostPane.add(clearButton); add(hostPane); } } public class ChatService { private List<ChatListener> listeners = new ArrayList<>(25); public void addChatListeners(ChatListener listener) { listeners.add(listener); } public void removeChatListener(ChatListener listener) { listeners.remove(listener); } protected void fireChatCleared() { if (listeners.isEmpty()) { return; } for (ChatListener listener : listeners) { listener.chatCleared(); } } public void clear() { // Do what's required fireChatCleared(); } } public interface ChatListener { public void chatCleared(); } public class PeopleAction extends AbstractAction implements ChatListener { private String name; private boolean raised; public PeopleAction(ChatService chatService, String name, boolean raised) { // You can use either LARGE_ICON_KEY or SMALL_ICON to set the icon this.name = name; if (raised) { putValue(NAME, "* " + name); } else { putValue(NAME, name); } chatService.addChatListeners(this); } public void setRaised(boolean raised) { if (raised) { putValue(NAME, "* " + name); } else { putValue(NAME, name); } } public boolean isRaised() { return raised; } #Override public void actionPerformed(ActionEvent evt) { // Do what ever needs to be done setRaised(!isRaised()); } #Override public void chatCleared() { setRaised(false); } } public class ClearAction extends AbstractAction { private ChatService chatService; public ClearAction(ChatService chatService) { this.chatService = chatService; putValue(NAME, "Clear"); } #Override public void actionPerformed(ActionEvent evt) { chatService.clear(); } } }
Item Listeners Error
I'm having an issue with Item Listeners.It's the first time I'm using it, so far all I've used is the Item Event. I was wondering if you could clear up what the difference between those two, as well point me out what I'm doing wrong. My issue is on line 46 the line starting with: Object source = toppingList.getSource(); and the error I get is 'Cannot find symbol'. I'm thinking I'm using the wrong item before the getSource();, I thought that the toppingList was the correct item, I can't see which other item I could put in it's place. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Pizza extends JFrame{ FlowLayout flow = new FlowLayout(); JComboBox pizzaBox = new JComboBox(); JLabel toppingList = new JLabel("Topping List"); JLabel aLabel = new JLabel("Paulos's American Pie"); JTextField totPrice = new JTextField(10); int[] pizzaPrice = {7,10,10,8,8,8,8}; int totalPrice = 0; String output; int pizzaNum; public Pizza() { super("Pizza List"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(flow); pizzaBox.addItemListener((ItemListener) this); add(toppingList); pizzaBox.addItem("cheese"); pizzaBox.addItem("sausage"); pizzaBox.addItem("pepperoni"); pizzaBox.addItem("onion"); pizzaBox.addItem("green pepper"); pizzaBox.addItem("green olive"); pizzaBox.addItem("black olive"); add(pizzaBox); add(aLabel); add(totPrice); } public static void main(String[] arguments) { JFrame frame = new DebugFourteen3(); frame.setSize(200, 150); frame.setVisible(true); } public void itemStateChanged(ItemEvent[] list) { Object source = toppingList.getSource(); if(source == pizzaBox) { int pizzaNum = pizzaBox.getSelectedIndex(); totalPrice = pizzaPrice[pizzaNum]; output = "Pizza Price $" + totalPrice; totPrice.setText(output); } } }
Gui elements do not have any getSource, it is a method of the event - telling you which gui element generated the event. But you know what the source of the event is, since in your constructor you wrote: pizzaBox.addItemListener((ItemListener) this); and you did not add this to any other gui element. So you cannot get events from any other gui element. So do not test for it. But there are other issues: Your PizzaBox should implement ItemListener: public class Pizza extends JFrame implement ItemListener and then just write pizzaBox.addItemListener(this); If you want to listen to multiple elements, add separate anonymous listener for each (and Pizza does not implement ItemListener) // in your constructor: pizzaBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { pizzaNum = pizzaBox.getSelectedIndex(); // in your code you have int pizzaNum but at the same time, pizzaNum is a class variable, probably an error // and so on } } }); or you can move the code to a separate method public class Pizza extends JFrame { public Pizza() { : pizzaBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { pizzaBox_itemStateChanged(e); } }); : } private void pizzaBox_itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { pizzaNum = pizzaBox.getSelectedIndex(); // and so on } } : }
You need to implement ItemListener with class. For details go through this tutorial public class Pizza extends JFrame implements ItemListener{ ..... public Pizza(){ pizzaBox.addItemListener(this);// Here this is enough .... } // itemStateChanged should write as follows public void itemStateChanged(ItemEvent e) { //It will be enable if checkbox is selected if (e.getStateChange() == ItemEvent.SELECTED) { int pizzaNum = pizzaBox.getSelectedIndex(); totalPrice = pizzaPrice[pizzaNum]; output = "Pizza Price $" + totalPrice; totPrice.setText(output); } } }
JFrame - JList specific actions depending on selection
I created a list basically copying the 'ListDemo.java' from the oracle site. I included a picture of what I have. public static class BackpackList extends JPanel implements ListSelectionListener { private JList list; private DefaultListModel listModel; private static final String useString = "Use"; private JButton useButton; public BackpackList() { super(new BorderLayout()); listModel = new DefaultListModel(); listModel.addElement("Flashlight"); listModel.addElement("Health potion"); listModel.addElement("Snacks"); //Create the list and put it in a scroll pane. list = new JList(listModel); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setSelectedIndex(0); list.addListSelectionListener(this); list.setVisibleRowCount(10); JScrollPane listScrollPane = new JScrollPane(list); useButton = new JButton(useString); useButton.setActionCommand(useString); useButton.addActionListener(new UseListener()); //Create a panel that uses BoxLayout. JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.add(useButton); buttonPane.add(Box.createHorizontalStrut(5)); buttonPane.add(new JSeparator(SwingConstants.VERTICAL)); buttonPane.add(Box.createHorizontalStrut(5)); buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); add(listScrollPane, BorderLayout.CENTER); add(buttonPane, BorderLayout.PAGE_END); } class UseListener implements ActionListener { public void actionPerformed(ActionEvent e) { //This method can be called only if //there's a valid selection //so go ahead and remove whatever's selected. int index = list.getSelectedIndex(); listModel.remove(index); int size = listModel.getSize(); if (size == 0) { //Nobody's left, disable firing. useButton.setEnabled(false); } else { //Select an index. if (index == listModel.getSize()) { //removed item in last position index--; } list.setSelectedIndex(index); list.ensureIndexIsVisible(index); } } } #Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (list.getSelectedIndex() == -1) { //No selection, disable fire button. useButton.setEnabled(false); } else { //Selection, enable the fire button. useButton.setEnabled(true); } } } } Question 1: I am setting up a backpack for a basic text based game. I want to set up specific actions depending on what item you have selected in the list. What would be the code to make it so the health potion would do something different than the snacks? Question 2: How could I make it so it would say something along the lines of "x2 Snacks" if you have 2 snacks or "x3 Snacks" if you have 3 snacks, etc.
The backpack needs to keep track of items that are defined elsewhere. JList can hold a list of any kind of object so what you need to do is create objects for the inventory items. Below shows an example using an enum: public class InventoryManager { public enum InventoryItem { LIGHT("Flashlight") { boolean isOn; #Override public void doAction() { isOn = !isOn; } #Override public String toString() { return name; } }, POTION("Health Potions") { #Override public void doAction() { Game.getPlayer().setHealth(Game.getPlayer().getHealth() + 25); remove(1); } }, SNACK("Snacks") { #Override public void doAction() { Game.getPlayer().setEnergy(Game.getPlayer().getEnergy() + 10); remove(1); } }; private final String name; private int quantity = 0; private InventoryItem(String n) { name = n; } public abstract void doAction(); public void add(int q) { if ((quantity += q) < 0) quantity = 0; } public void remove(int q) { add(-q); } #Override public String toString() { return name + " x" + quantity; } } public static InventoryItem[] getHeldItems() { EnumSet<InventoryItem> items = EnumSet.allOf(InventoryItem.class); Iterator<InventoryItem> it = items.iterator(); while (it.hasNext()) { if (it.next().quantity < 1) { it.remove(); } } return items.toArray(new InventoryItem[items.size()]); } } The enum example is entirely static so there are some problems with actually doing it this way (I chose it primarily because it's the shortest code). But ultimately what you'll have is a superclass Item with abstract methods that the subclasses implement differently. Then you will populate the JList with the items held. When the user selects an item from the list, list.getSelectedValue() returns an Item object you can use in the game. // or Item can be an interface classes implement public abstract class Item { public void doAction() { Game.updateState(); } } public class Light extends InventoryItem { boolean lighted; #Override public void doAction() { lighted = !lighted; super.doAction(); } } public class Potion extends InventoryItem { #Override public void doAction() { player.hp++; super.doAction(); } } public class Snack extends InventoryItem { #Override public void doAction() { player.energy++; super.doAction(); } } The other way to do this is to use straight program logic, for example: switch (list.getSelectedItem()) { case "Flashlight": { toggleFlashlight(); break; } case "Health Potion": { usePotion(); break; } case "Snack": { useSnack(); break; } } But I have a feeling trying to do it all with logic like that will ultimately turn out to be more complicated.