update JcomboBox after Array is updated - java

I have a JComboBox which displays the contents of an array in my main class, but I have a another class that has a function which changes the array based on user inputs. However the JComboBox does not update even though the array has been updated in the main class (I used a print to check that it does in fact update). Is there a way for the JComboBox to update as more items are added to the array or items are removed from the array?
This is the JComboBox in the main class, where buildingNames is the array storing information and will be updated.
private String[] buildingNames;
public mainWindow() {
initialize();
}
private void initialize() {
frame = new JFrame("Main Window");
frame.setBounds(0, 0, 1280, 720);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setBackground(Color.WHITE);
frame.setResizable(false);
buildingNames = {"Costlemark","Science","Research"} //This will get updated
DefaultComboBoxModel BuildingModel = new DefaultComboBoxModel(buildingNames);
JComboBox selectBuilding = new JComboBox(BuildingModel);
selectBuilding.setBounds(46, 82, 150, 40);
frame.getContentPane().add(selectBuilding);
}

Several solutions exist including:
Use an observer pattern to notify concerned objects when the array is updated, and then create a new model for the combo box, and load it into the combo when updates occur. This would be part of a larger Model-View-Controller program structure and is probably the way I'd go.
Create your own model class, extending the abstract combo box model class, one that uses the array itself, and one that again is notified when the array is changed.
Get rid of the array altogether and instead update the combo box model when and where needed
The details of any solution, including code would depend on the details of your current program.
Side recommendations:
Your combo box variable should not be declared locally within the initialize() method as that will make it invisible to the rest of the class, nor should any other object be assigned to a local variable that needs to have its state changed by the program. Declare the variable as a private instance field of the class.
Never set the bounds of your components or use null layouts but rather set properties (visible row count, prototype display value...) and allow the component to size itself.
If your array contents are likely going to change quite a bit during program ru, then you probably should be using a collection such as an ArrayList<String>, or even better, an ArrayList<Building> of your custom Building class.
For an example of the last recommendation where we just use the combo box model:
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class BuildingTest extends JPanel {
// model to hold all the building name strings
private DefaultComboBoxModel<String> comboModel = new DefaultComboBoxModel<>(new String[] {"Foo", "Bar", "Baz"});
private JComboBox<String> selectBuildingCombo = new JComboBox<>(comboModel);
// text field to allow user to add more strings to model
private JTextField entryField = new JTextField(10);
private JButton enterBuildingBtn = new JButton("Enter Building Name");
public BuildingTest() {
// the size of the combobox larger
selectBuildingCombo.setPrototypeDisplayValue("abcdefghijklmnopqrstuv");
add(selectBuildingCombo);
add(new JLabel("Enter new building name:"));
add(entryField);
add(enterBuildingBtn);
selectBuildingCombo.addActionListener(e -> {
String selection = (String) selectBuildingCombo.getSelectedItem();
if (selection != null) {
System.out.println("Selected Item: " + selection);
}
});
// occurs when user wants to add to combo model
ActionListener enterBuildingListener = e -> {
// get text from text field
String text = entryField.getText().trim();
if (!text.isEmpty()) {
// if not empty, add to model
comboModel.addElement(text);
entryField.selectAll();
}
};
// add this action listener to both the text field and the button
enterBuildingBtn.addActionListener(enterBuildingListener);
entryField.addActionListener(enterBuildingListener);
enterBuildingBtn.setMnemonic(KeyEvent.VK_E);
}
private static void createAndShowGui() {
BuildingTest mainPanel = new BuildingTest();
JFrame frame = new JFrame("Building Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

Related

Style errors with swing: method creates an object but does not assign variable or field

"This method creates an object but does not assign this object to any variable or field. This implies that the class operates through side effects in the constructor, which is a bad pattern to use, as it adds unnecessary coupling. Consider pulling the side effect out of the constructor, into a separate method, or into the calling method."
This short test program runs as I expected, but I don't know how to address this checkstyle error. Most of the examples of using javax.swing seem to have this structure.
There is also a error causes by EXIT_ON_CLOSE, but without it the process lingers after I close the window and must be force quit.
public class GUI implements ActionListener {
private int clicks = 0;
private JLabel label = new JLabel("Clicks= " + clicks);
private JFrame frame = new JFrame();
public GUI() {
// make a Jbutton named button
JButton button = new JButton("Click Me");
button.addActionListener(this);
// arrange the button and label
JPanel panel = new JPanel();
panel.add(button);
panel.add(label);
// put the panel in a frame
frame.add(panel, BorderLayout.CENTER);
// EXIT_ON_CLOSE has a style error too.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Graphical User Interface");
frame.pack();
frame.setVisible(true);
}
// update label and number of clicks when button is clicked
public void actionPerformed(ActionEvent e) {
clicks++;
label.setText("Clicks= " + clicks);
}
// This is the code that InteliJ says has bad form.
public static void main(String[] args) {
new GUI();
} }
One bad pattern here is that you are not creating your GUI on Event Dispatch Thread. Not sure if this is related to your problem.. but you should always use EDT to create the GUI in Swing.
EDIT: take a look here. In the main method you just have "new GUI()" without a reference variable. Writing your app like this example you are not going to use the constructor to create all :)

How to find the text of anonymous jtextfield

So i am making a shop system with a gui. I have a menu item that when i press it opens another jframe to input the number of each item sold in a jtextfield, like this:
JPanel salesPanel = new JPanel();
setSize(new Dimension(520,270));
setResizable(false);
setLocation(200,200);
title = new JLabel("<html><u><b>Fill in the number of products sold.</b></u></html>");
salesPanel.setSize(new Dimension(230,30*sw.getProductList().size()));
salesPanel.setLayout(new GridLayout(sw.getProductList().size()+1,3));
...
sw.getProductList().forEach(n ->{
salesPanel.add(new JLabel(Integer.toString(n.getProductID())+":"));
salesPanel.add(new JLabel(Integer.toString(n.getQuantity())));
salesPanel.add(new JLabel(n.getName()));
salesPanel.add(new JTextField());
});
This is how it looks.
Note that sw is the object of the main class which has an ArrayList of the type product which contains the information of each product.
Is there any way that I can text from these JTextFields ? And if not what is another way that I can do this.
EDIT:
in the main ShopWindow class, I have an ArrayList
private ArrayList<Product> productList = new ArrayList<Product>();
class product:
public class Product {
private int productID;
private String name;
private double price;
private int quantity;
private boolean isPerishable;
private double totalProdValue;
...getters and setters for each field
This is a mock solution (only meant to show how to update qty label and clear fields using action listeners)
public class MockFrame {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel framePanel = new JPanel();
ProductPanel bananaPanel = new ProductPanel("268", "25", "Bananas");
ProductPanel sugarPanel = new ProductPanel("321", "200", "Sugar");
JPanel buttonPanel = new JPanel();
JButton update = new JButton("Update");
JButton cancel = new JButton("Cancel");
buttonPanel.setSize(400, 30);
update.setSize(50, 20);
cancel.setSize(50, 20);
buttonPanel.add(update);
buttonPanel.add(cancel);
update.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bananaPanel.setNewQty();
sugarPanel.setNewQty();
}
});
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bananaPanel.clearField();
sugarPanel.clearField();
}
});
frame.setSize(400, 400);
framePanel.setLayout(new BoxLayout(framePanel, BoxLayout.PAGE_AXIS));
framePanel.add(bananaPanel);
framePanel.add(sugarPanel);
framePanel.add(buttonPanel);
frame.add(framePanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class ProductPanel extends JPanel {
private JLabel productId = new JLabel();
private JLabel qty = new JLabel();
private JLabel name = new JLabel();
private JTextField field = new JTextField();
public ProductPanel(String id, String amount, String itemName) {
this.setSize(400, 30);
this.field.setSize(100, 20);
this.field.setColumns(5);
productId.setText(id);
qty.setText(amount);
name.setText(itemName);
this.add(productId);
this.add(qty);
this.add(name);
this.add(field);
}
public void clearField() {
field.setText("");
}
public void setNewQty() {
String newQty = field.getText();
if (newQty != null && !newQty.isBlank()) {
qty.setText(newQty);
}
}
}
}
Main points of this mocked solution:
Use a JPanel to encapsulate a product line item. This will make it easier if you need to remove and/or add product rows.
The product panel contains a method to update qty or clear the fields that will be invokable by the frame buttons (depending on which is clicked).
Simplicity of design - Creating a generic panel for the product eliminate repetitive code.
Obviously, you would have to modify this so that you use the proper layout manager or use absolute positioning to properly aligned components to your liking. Also, you would need to create a Panel for the table header and add the remaining of your products. Also, you may want to break this into public classes and even maybe create a separate class for your frame.
The action listeners could also have a "for-each" loop to update each ProductPanel instead of hard coding each panel individually. That should look something like this:
public void actionPerformed(ActionEvent e) {
JPanel panel = (JPanel)((JButton) e.getSource()).getParent().getParent();
Component[] components = panel.getComponents();
for (Component c : components) {
if (c instanceof ProductPanel) {
((ProductPanel)c).setNewQty();
}
}
}
});
Obviously, your solution will depend on how you decide to encapsulate your components in containers. For this mock, the product panels are inside the frame panel which contains the panels where the buttons were placed. Therefore, I need to get the "grandparent" container for the update and cancel buttons to take advantage of calling the appropriate methods to update and clear in a more dynamic way.
Lastly, you may want to do something more elegant for creating your product panels. For example, you may want to add some factory method to create your product panel instead of having hard-coded product panels like my mock solution. Anyway, I think I demonstrated the solution you were looking for.
UPDATE: If you don't follow Andrew Thompson's recommendation of not using text fields for numeric values, the panel's getNewQty method would need to validate the text obtained to make sure it contains a valid numeric value (which was his point). I would STRONGLY recommend you follow his advice.

Cannot refer to the non-final local variable display defined in an enclosing scope

This might be a very basic question. But I am stuck at this. The error that I get for the String variable display states:
Cannot refer to the non-final local variable display defined in an enclosing scope.
If I use a final keyword, I get the message:
The final local variable display cannot be assigned, since it is defined in an enclosing slope.*
The code is:
public class Frame {
public static void main(String[] args) {
String display=" ";
Frame ob=new Frame();
JFrame frame=new JFrame("Test");
frame.setBounds(300,100,800,500);
//Container c=frame.getContentPane();
frame.setLayout(null);
final JTextField name=new JTextField();
name.setBounds(500,212,150,20);
JLabel nameLabel=new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
nameLabel.setBounds(450,171,100,100);
JTextField ohr=new JTextField();
ohr.setBounds(500,282,150,20);
JLabel ohrID=new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
ohrID.setBounds(450,241,100,100);
final JButton button=new JButton("Submit");
button.setBounds(530,350,90,20);
frame.add(name);
frame.add(ohr);
frame.add(ohrID);
frame.add(nameLabel);
frame.add(button);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.setVisible(true);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if(e.getSource()==button){
display=name.getText();
JOptionPane.showMessageDialog(null, "Hi "+ display);
System.exit(0);
}
}
});
}
Thanks in advance!
There are multiple issues with your code, and we'll address them right here, right now and solve your problem at the same time.
public class Frame { this particular line has an error, Frame is the name of an AWT class, so it might confuse you or anyone who reads this code later on, give it a more meaningful name and avoid those names that could be confused with other Java packages.
Frame ob=new Frame(); you create an instance of your class and never use it again, why?
frame.setLayout(null); NEVER, please don't use null-layout, Swing has to deal with multiple PLAFs, screen sizes and resolutions, different OS, pixel perfect apps might seem like the easiest way to create complex UIs but later on you'll find that errors like this happen very often.
.setBounds(...) on every component, again, this is due to null-layout but it's better to use Layout managers
final JTextField name=new JTextField(); There's no need to declare any of your components as final, this is due to a poor design of your class, your components should be declared as class members (outside any method including main).
Speaking about main, separate your program into smaller pieces, don't throw everything at main or at the very least create a method that is not static so you can call it after creating an instance of your class (or else later on you'll end up with tons of static variables and that's a poor design of your class once again).
System.exit(0); it will stop the JVM, it's never a good idea to do that, it's better to .dispose() the JFrame and have your JFrame's defaultCloseOperation set to EXIT_ON_CLOSE which will safely dispose your app and then stop the JVM.
display=name.getText();, for this particular case, display could be an inner variable rather than a class member. This will solve your particular question
JOptionPane.showMessageDialog(null, "Hi "+ display); that null should be a reference to your JFrame, this will place your dialog in the middle of that JFrame rather than in the middle of the screen.
You never place your program inside the EDT, see point #2 in this answer.
So, having all the above points in mind, here's an improved version of your code.
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class UsingVariablesInsideActionListenerExample {
//We declare our components here
private JFrame frame;
private JButton button;
private JTextField name;
private JTextField ohr;
private JLabel nameLabel;
private JLabel ohrID;
private JPanel pane;
private JPanel namePane;
private JPanel ohrPane;
public static void main(String[] args) {
SwingUtilities.invokeLater(new UsingVariablesInsideActionListenerExample()::createAndShowGUI); //This is using Java 8 lambdas to place your program in the EDT
}
private void createAndShowGUI() {
frame = new JFrame("Test"); //Create your JFrame
pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); //This will make this JPanel to arrange components vertically
namePane = new JPanel(); //By default, JPanels have FlowLayout which will arrange components horizontally
ohrPane = new JPanel();
name = new JTextField(10); //We create a JTextField with 10 columns
nameLabel = new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
ohr = new JTextField(10);
ohrID = new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
button = new JButton("Submit");
//Add the action listener
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
String display = name.getText(); //The display variable is now an inner variable rather than a class member
JOptionPane.showMessageDialog(frame, "Hi " + display);
frame.dispose(); //We dispose the JFrame and it will be closed after due to EXIT_ON_CLOSE below.
}
}
});
//We add the components to the namePane (horizontally), the order matters
namePane.add(nameLabel);
namePane.add(name);
//Now we add these components to the ohrPane (horizontally again)
ohrPane.add(ohrID);
ohrPane.add(ohr);
//We then add the name and ohr panes to a bigger JPanel (pane, which if you remember will add them vertically) and we add the button at the end
pane.add(namePane);
pane.add(ohrPane);
pane.add(button);
//We make them non opaque (transparent) so that we can see the background color of the JFrame
namePane.setOpaque(false);
ohrPane.setOpaque(false);
pane.setOpaque(false);
frame.add(pane);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.pack(); //This will get every component's preferred size and make the JFrame as small as possible where it looks good on every OS, PLAF, screen size and resolution.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //We make the frame visible (always at the very end, when we've added everything to it).
}
}
And this is how it looks like now.
The UI may not be perfectly equal to the one you have, but I'm sure you can play with the different layout managers, and nest various JPanels to get a much better looking UI than mine, or at least a more similar one to the one you had.
Variable used in side an inner class should be effectively final . You can use a string[] of length 1 instead of string to resolve this . Please read bellow post for more details
Difference between final and effectively final
Also check this post for more details
Variable used in lambda expression should be final or effectively final

How to access variable from action listener/class constructor to use in main method in java

I have a frame with a combo box that displays different shapes and a button, for the button I added an action listener which will get the selected item from the combo box and store it as a string which i declared as a public class variable, in my main method i want to access this string to make a finch robot draw that shape but I can't seem to access it no matter what I try
public class DrawShape
{
private JFrame frame;
private String[] choices = {"circle", "square", "triangle", "rectangle", "quit"};
public String choice = "";
//class constructor
public DrawShape()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
JPanel p = new JPanel();
final JComboBox cb = new JComboBox(choices);
JButton button = new JButton("Done");
p.add(cb);
p.add(button);
frame.add(p);
//create an action listener that, when button is clicked, gets the selected choice and stores it to
//the string variable 'choice'
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
choice = (String)cb.getSelectedItem();
}
}) ;
frame.pack();
}
public static void main(String[] args)
{
new DrawShape();
System.out.println(choice);
}
}
I wouldn't recommend the use of non-private variables. However, you need to keep a reference to the object you created and then access the fields through that reference as if you were calling methods on an object.
DrawShape draw = new DrawShape();
System.out.println(draw.choice);
However, you should see null as this is called immediately after you construct the object rather than from the listener.
You probably want the code executed from the listener. So either put the print code in the listener, or have the listener call another method with that in.
GUI programming tends to be event driven. Don't expect to be able to sequence the user interaction - the user drives.
you should use getters/setters in this case. Your action listener would call the getter method which would in turn get what is in the combobox.
Here is an example of how that works: https://www.codejava.net/coding/java-getter-and-setter-tutorial-from-basics-to-best-practices
Hope this helps.

add Component to JPanel

I am using netBeans editor to create desktop application . and i want to add Component without using drag and drop way. I am trying code like this for adding JList to JPanel but nothing showed
JList jl = new JList();
Vector<String> v= new Vector<String>();
v.add("one");
v.add("Two");
v.add("Three");
jl.setListData(v);
JScrollPane js = new JScrollPane(jl);
js.setLocation(50, 50);
js.setSize(100, 100);
js.setVisible(true);
jPanel1.add(js);
I want to add Component without using drag and drop way.
Here's a simple JList example that doesn't use the NetBeans' GUI editor. See How to Use Lists for more.
import java.awt.*;
import java.util.Random;
import javax.swing.*;
public class JListTest {
private static final Random random = new Random();
public static final void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
final DefaultListModel dlm = new DefaultListModel();
for (int i = 0; i < 1000; i++) {
dlm.addElement("Z" + (random.nextInt(9000) + 1000));
}
final JList list = new JList(dlm);
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new JScrollPane(list), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
The scroll list doesn't appear, or the data items in the list? Also, you're setting the position manually. Seriously, don't do that -- use a layout manager, many of which are available and you can easily use in the Netbeans GUI editor Mattise.
If the main window is under the control of a layout manager and then you add something to it that specifies its position and size, all mayhem will break loose. Namely, the layout manager will overwrite this, possibly with the result of size becoming 0, 0.
What you need to do is create a JPanel in your layout manager to hold the position of the new component and make sure it has a known field name you can reference and use to add to. Make sure that Panel also has FlowLayout or something in the properties.
you may want to call repaint() when you dynamically create GUI elements.

Categories