1) In the following method (actionListener) a user select a grade (e.g. A-F) from a JComboBox.
2) There are multiple JComboBoxes, and each selection made gets stored into a single String[] array.
PROBLEM:
Here is the dilemma, if a user goes back and changes a selection made from a random JComboBox the previous grade selection does not get replaced in the array, however the new selection made gets stored at the next array index.
How can I make the program replace the previous grade selection and not just add the new selection?
relevant variables:
int counter;
private JComboBox[] gradeField;
//grade.userGrades[] is array of grades taken from selected combo boxes
Action Listener anonymous class:
gradeField[counter].addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e) {
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox)holder;
String theGrade = (String)tempGradeBox.getSelectedItem();
grade.userGrades[grade.getNext()] = theGrade;
grade.updateNext();
}
});
Thanks in advance for any help.
I save the grade in an array and increment the index,
Well you should not be incrementing the index. This assumes that the user selects the grades from the combo box in a sequential order. As you have discovered users can often work randomly.
Instead you need to know which combo box has been changed and then update the appropriate entry in your array.
Or a different solution might be to update your array at the end. So maybe you have a "Process Results" button. Then you can sequentually loop through all the combo boxes to get the selected value.
Update the user grade being at the same index as the combo box:
final int index = counter;
gradeField[counter].addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e) {
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox)holder;
String theGrade = (String)tempGradeBox.getSelectedItem();
grade.userGrades[index] = theGrade;
}
});
Here's another variation of JB Nizet's answer:
class OuterClass
{
...
gradeField[counter].addActionListener( new GradeSettingActionListener( counter ) );
...
class GradeSettingActionListener implements ActionListener
{
// -- Doesn't have to be final here (it does in JB's answer), but I like to be restrictive.
private final int index;
public GradeSettingActionListener( int index )
{
this.index = index;
}
#Override
public void actionPerformed( ActionEvent e )
{
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox) holder;
String theGrade = (String) tempGradeBox.getSelectedItem();
grade.userGrades[index] = theGrade;
}
}
}
This approach removes the anonymous class by adding an inner class. The inner class will still have access to grade. You don't gain much here unless there's a chance you'll be splitting out the inner class later.
Of course, camickr's suggestion to process all the grades at once may also be valid, depending on other requirements (i.e., whether additional processing is done after the grades are stored in the array, which seems likely).
Related
I am trying to build a GUI application that will let the user to choose product by clicking the button. I hold products in an ArrayList and then use this ArrayList and for loop to create proper number of JButtons. When user clicks the button price of that product should appear in the TextField.
My problem is: how to find out which button was clicked? If I was using Array of Buttons (JButton button[] = new JButton[3]) I would find it in the loop:
if (target.equals(button[i]))...
But I can't figure out how to find it when I use ArrayList of products to create buttons. Any help would be well appreciated. Here's my code (I tried many approaches so I only post the one I started with - it finds only the last item in the ArrayList).
public void addStuff() {
stuffList.add(new Stuff("Lemon Haze", 15.00));
stuffList.add(new Stuff("OG Kush", 16.00));
stuffList.add(new Stuff("Strawberry Cough", 18.00));
for (int i = 0; i < stuffList.size(); i++) {
stuffButton = new JButton();
stuffPanel.add(stuffButton);
stuffButton.setText(stuffList.get(i).getName());
stuffButton.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
Object target = e.getSource();
for (int i = 0; i < stuffList.size(); i++) {
if (target == stuffButton) {
subtotalTextF.setText(stuffList.get(i).getPrice() + "");
}
}
}
Create a specific class for your ActionListener, and give it a reference to your Stuff - this way you can create a specific instance for each button that automatically links back to the correct instance of Stuff, without trying to search on the fly:
stuffButton.addActionListener(new StuffListener(stuffList.get(i));
...
private class StuffListener implements ActionListener {
private final Stuff myStuff;
public StuffListener(Stuff stuff) {
this.myStuff = stuff;
}
public void actionPerformed(ActionEvent e) {
subtotalTextF.setText(String.valueOf(myStuff.getPrice()));
}
}
Note that you can accomplish this with a bit less code using lambdas, but figured this is the clearest way to explain the logic, which is the same either way.
On a side note, based on the code you've posted, the reason it's only getting the last button is because you're comparing to stuffButton, which is not changed from the last instance after your initialization loop is done.
I have created buttons inside a panel using Java Swing. I have a button "Produce" that when clicked, I need it to create an object from type Produce (which is defined in another class and has items such as vendor, weight, and price).
When the button is clicked, the Produce object should be created using information that a user has typed in to the text fields in the same panel. So if a user typed in the vendor, weight, and price for an item into the textfields, the Produce object needs to be created with those values.
So far I have:
public void createButtons() {
JButton produceBtn = new JButton("Produce");
JButton prepMealBtn = new JButton("Prepared Meal");
infoPanel.add(produceBtn, BorderLayout.SOUTH);
infoPanel.add(prepMealBtn, BorderLayout.SOUTH);
produceBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
if(source == produceBtn){
Produce myProduce = new Produce();
}
}
}
}
But I can't figure out how to do the mentioned part above.
the Produce object should be created using information that a user has
typed in to the text fields in the same panel. So if a user typed in
the vendor, weight, and price for an item into the textfields, the
Produce object needs to be created with those values.
First, you'll need to create some sort of list/array to hold the created objects.
List<Produce> produce = new ArrayList<>(); // make this global within the class
then, you have two options, either overload the Produce constructor and create another constructor to take 3 params (vendor, weight, and price):
Example constructor:
public Produce(type param1, type param2, type param3){ //Constructor to take 3 params
// assign the values appropriately
}
or create setter methods:
public void setVender(type vender){
// assign the values appropriately
}
public void setWeight(type weigth){
// assign the values appropriately
}
public void setPrice(type price){
// assign the values appropriately
}
then you can do this:
if(source == produceBtn){
String vender = someTextField.getText();
String weight = someTextField.getText();
String price = someTextField.getText();
//perform any conversion from string to numbers if needed.
/*
*/
//then create the object
Produce myProduce = new Produce(vender,weight,price);
// make sure the order in which you input the data into the arguments of the constructor above is the same as the order in which the constructor definition of the Produce class is.
produce.add(myProduce);
}
or you can use the setter methods.
Lastly but not least, you seem to have a typo with addActionListener, you're missing the closing ).
Also, you can simplify your code by using lambda expression:
produceBtn.addActionListener(e -> {
Object source = e.getSource();
if(source == produceBtn){
// do something
}
});
I have a JComboBox that I made this way, using an enum for its values:
JComboBox<StudyGrade> maxLevelOfStudiesCombo = new JComboBox<StudyGrade>(StudyGrade.values());
The enum looks like this:
public enum StudyGrade {
ELEMENTARY ("Primaria"),
MIDDLE ("Secundaria"),
MIDDLE_HIGH ("Preparatoria"),
HIGH ("Universidad"),
MASTERS ("MaestrÃa / Posgrado"),
DOCTORATE ("Doctorado"),
POST_DOCTORATE ("Post Doctorado");
private String studies;
private StudyGrade(String studies) {
this.studies = studies;
}
public String getStudies() {
return studies;
}
public void setStudies(String studies) {
this.studies = studies;
}
#Override
public String toString() {
return studies;
}
}
As you can see I'm overriding the toString() method, so I can have the studies values shown instead of the enum ones...
However I want to show the studies values only in the JComboBox not everytime I use the StudyGrade enum.
How would I change the code, so whenever I use something like:
System.out.println(StudyGrade.HIGH);
I get printed HIGH instead of Universidad, but not for the JComboBox?
I'm overriding the toString() method, so I can have the studies values shown instead of the enum ones...
I've never used a enum before but I assume you can use it like any custom object added to the combo box so you should be able to use a custom renderer so you can control which data is displayed by the combo box.
Check out Combo Box With Custom Renderer for more information and a helper class.
You're looking to extend an enum, but that's impossible. It means that something is wrong with your requirement.
Rendering is done in the UI component, and it's not enum's business to deal with presentation of data. You should make you UI component render enum the way you'd like instead of trying to make enum understand where it's being used. Since you're a Swing fanatic you should know how to do that, something like this:
maxLevelOfStudiesCombo.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList<?> jList, Object o, int i, boolean b, boolean b1) {
Component rendererComponent = super.getListCellRendererComponent(jList, o, i, b, b1);
setText(o instanceof StudyGrade ? ((StudyGrade) o).getStudies() : o.toString());
return rendererComponent;
}
});
That's going to do that.
You could just remove the toString override as the default toString for an enum is to return the name of the enum element.
And you could just have a simple for loop that would iterate through the values in your enums and add it to a string array. Then, you would need to pass that array as the argument for your JComboBox and it should be gold.
The code for it should look a bit like that:
//get all study grades
StudyGrade[] temp = StudyGrade.values();
//create a string array of same length as the array
String[] str = new String[temp.length];
//append all the studies value to the string array
for(int i = 0; i< temp.length; i++){
str[i] = temp[i].getStudies();
System.out.println(temp[i]);//debug
}
System.out.println("---------------------");//debug
for(String s : str){//debug
System.out.println(s);//debug
}//debug
//pass it
JComboBox<StudyGrade> maxLevelOfStudiesCombo = new JComboBox<StudyGrade>(StudyGrade.values());
Here is an example I made on repl.it
https://repl.it/GH28/1
I see there are some questions already asked about this topic but I haven't come up to an answer for my. I am writing a code where user types something in JTextField, and after clicking a button, his word is replaced with the number of asterisks with the same number of characters his word had e.g "table" would be replaced by "****". i did it this way:
ask.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String guess = "";
String given = textGive.getText();
for (int i=0; i<given.length(); i++){
String asterisk = "*";
guess += asterisk;
textGive.setText(guess);
}
}
});
I know that I did not do this in a great way, but I did not know how to do it better. Any recommendations?
Now, I want somehow to save both Strings, the original word and the asterisk one outside the scope so I can access it in another ActionListener and modify it further.
Before writing first ActionListener i did write String guess = "" and String given = "" but it seems as it did not do anything.
So, in my second ActionListener i want to send him the string given I received when the user typed his word.
guess.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String attempt = textGuess.getText();
char att = attempt.charAt(0);
for (int i=0; i<5; i++){
if (given.charAt(i)==att){
textGuess.setText("Ok!");
}
}
}
});
Eclipse gives me error saying:
"Cannot refer to the non-final local variable given defined in an enclosing scope".
I understand that I need to make given final in order to acces it further, but how to do that if the variable depends on text input from first ActionListener? Is there some other solution for this problem? I've recently started using java, so I do not know the language so well.
Anything that you want visible to class should be placed in instance fields, not in local variables. For instance, the given variable should be a private non-static field declared in the class, and not a variable buried within your listener's actionPerformed method.
e.g.,
public class Foo extends JPanel {
private JButton ask = new JButton("Ask");
private JTextField textGive = new JTextField(10);
private String given = ""; // visible throughout the class
public Foo() {
add(textGive);
add(ask);
ActionListener listener = e -> {
String guess = "";
// String given = textGive.getText(); //visible only within this method
given = textGive.getText();
guess = given.replaceAll("\\w", "*");
textGive.setText(guess);
};
ask.addActionListener(listener);
textGive.addActionListener(listener); // also give the same listener to the JTextField
}
I'm trying to get the position (as a int) of a JComboBox object, The ComboBox is generated and haves an action listener like this
for (int d=0; d<i; d++)
{
titulos.addItem(listaPraBox[d]);
}
ActionListener comboListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
ItemSelectable is =(ItemSelectable)actionEvent.getSource();
objectoseleccionado = selectedString(is);
DeskMetodos.listaTexto(objectoseleccionado);
}
};
titulos.addActionListener(comboListener);
The executes
static private String selectedString(ItemSelectable is) {
Object selected[] = is.getSelectedObjects();
return ((selected.length == 0) ? "null" : (String)selected[0]);
}
But I wanted the position of the selected object to get a string from another array by that int.
Is this even possible? By the search I've made there isn't even reference to this.
JComboBox defines getSelectedIndex(). The implementation is just to loop over the data model checking equality with getSelectedItem().
That doesn't make it up into ItemSelectable, but neither does the data model itself, so you may need to use the concrete class.
Instead of storing items in a ComboBox and having to use the index to reference another array of values. Just store an object in the ComboBox that has a toString() output that matches your current displayed value and a direct reference to the object in the array. That way any object pulling the selected item or dealing with comobo box can just pull the value they need and not have to also "know" about this other array.