I made this sample below to simulate multiple JCheckBox creation and its Action Listener.
int global=0;
//some code
JCheckBox[] checkBox = new JCheckBox[2];
for(int i = 0; i <=1; i++){
checkBox[i] = new JCheckBox(strings[i]);
panel.add(checkBox[i]);
checkBox[i].addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED){
JOptionPane.showConfirmDialog(null, "Message"+global);
}
}
});
global++;
}
What I'm not getting is that my output for the Dialog is always "Message 2". In my logic if I'm declaring one AddItemListener for each checkBox, I should recieve two different dialogs for each checked box, such as "Message 1" and "Message 2". What am I doing wrong here? How to handle this please?
Thanks in advance
When showConfirmDialog() is first called global has already value 2. If you want different message for each check-box try putting global++ (will increment at each call) right before JOptionPane.showConfirmDialog(null, "Message"+global); and this will make it more clear to you.
if I'm declaring one AddItemListener for each checkBox, I should
recieve two different dialogs for each checked box, such as "Message
1" and "Message 2"
Why do you think you should get two (different) invocations of listener method per checkbox if you know that you have only one listener per checkbox?
One of the more possible solutions could be to implement your own ItemListener which has stored the message (or just number) to be shown in its instance variable.
global is nowhere related to the JChekcbox objects that you are creating in your code. So , Whenever itemStateChanged is called by the application, It is reading the latest value of global which is 2. To achieve whatever you are looking for you should change your code in this way:
for(int i = 0; i <=1; i++){
checkBox[i] = new JCheckBox(strings[i]);
panel.add(checkBox[i]);
checkBox[i].addActionCommand(String.valueOf(i+1));
checkBox[i].addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED){
JOptionPane.showConfirmDialog(null, "Message"+((JCheckBox)evt.getSource()).getActionCommand());
}
}
});
global++;
}
In your code global is increased by 1 at each iteration of the loop. Value of global is 2 after loop exits that is why you have "Message 2". If these numbers represent the location in the array then I would try:
#Override
public void itemStateChanged(ItemEvent evt) {
int loc = indexInArray(evt.getItem(),checkBox);
if (evt.getStateChange() == ItemEvent.SELECTED){
JOptionPane.showConfirmDialog(null, "Message"+global);
}
}
});
You can implement a simple search in the method indexInArray:
public int indexInArray(Object []objects, Object obj){
for(int i = 0 ; i < objects.length; i++){
if(objects[i] == obj){
return i;
}
}
return -1;
}
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.
private int var = 0;
test(){
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String selection = (String) items.getSelectedItem();
for (int i = 0; i < itms.length; i++) {
if (selection == itms[i]) {
var = 10 + i;
System.out.println(var); // prints the desired value
}
}
}
};
System.out.println(var); // prints 0 but why not desired value???
}
This actionListener is for a combo box. I want to take the value of the selected item in the combo box and give that to another actionListener which will append a new value to the var from the original actionListener based on which JButton is selected. How can I get the value of var from inside this actionListener and use it in another actionListener that is also in the same constructor? Is that even possible? Is there a better approach?
Your actionPerformed() method will get executed as when its event occurs, but this is not the case with print statement outside the actionPerformed().
So this statement
System.out.println(var); // prints 0 but why not desired value???
gets executed whenever you create an object of your test class (It is preferred to name it Test class according to Java naming conventions) because the print statement is written inside the constructor. In contrast, your print statement inside the actionPerformed method will get executed and print the correct value which you is your "desired value", whenever the event occurs.
Here is what I am trying to do: I have a swing gui with two JFrames. The first has a JCheckBox and the second displays some text. Also the second has a javax.swing.Timer that is waiting for the checkbox in the first frame to be clicked. Once it is clicked, some more text is to be displayed. It works if I have only one condition (click the checkbox) and the condition is directly in the if-statement, like this:
javax.swing.Timer timer = new javax.swing.Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ( otherGUI.jCheckBox.isSelected() ){
//add some text to second JFrame
timer.stop();
}
}
});
Now for the twist: This should happen not only once, but multiple times. There is an ArrayList of timers, each with its own text and condition, one starting after the other. My problem is: If I store the conditions as strings in an ArrayList, they seem to be evaluated once at the start of the programme, so the condition from above stays false, even when I click the checkbox. Here is my actual code:
SomeGUI gui = new SomeGUI();
ArrayList<javax.swing.Timer> timer = new ArrayList<javax.swing.Timer>();
ArrayList<String> text = new ArrayList<String>();
ArrayList<String> cond = new ArrayList<String>();
text.add("some text");
cond.add("gui.jCheckBox.isSelected()");
text.add("some more text");
cond.add(new Condition("true"));
//etc.
for ( int i = 0; i < text.size() - 1; i++ ){
int j = i;//not sure why this trick is necessary. i doesn't work later on
timer.add( new javax.swing.Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean bool = false;
try{
bool = Boolean.parseBoolean( cond.get(j) );
}
catch(Exception ex){}
if ( bool ){
addText(p, text.get(j+1));
timer.get(j).stop();
timer.get(j+1).start();
}
}
}));
}
timer.get(0).start();
I already tried an ArrayList<Boolean> for the conditions to the same effect. The code above just represents my present state of trial and error.
I hope that I could make clear what I am trying to achieve. So how can I store boolean expressions in a list/array and have them evaluated in an if-statement again and again at runtime and not only once when the programme is started?
There is no simple "evaluation" of strings in Java. It is possible, but really not "java style". See Convert String to Code
One other option would be that your strings represent method names (which exist on a well known object); then you could use reflection to invoke that method based on the provided string. But also, pretty ugly.
I have a 2D-Array of JButtons
JButton[][] ledBtns = new JButton[8][8];
And in a loop, I do all the init stuff. Now I want to add an EventListener to each JButton, that fires when the Button os clicked. Then I want to change the image on the Button.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage();
}
});
}
}
Now 'changeImage()' will be called, but I need to know what button called it.
I can't use parameters, if I do it tells me to declare them as 'final'.
Is there any other way than writing 64 methods, that do exactly the same, and adding them manually to each of the JButtons?
The ActionEvent class has a getSource() method used to get the component that generated the event.
The easiest way to do this is to just declare two temporary final ints, and reference those.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
final int finalI = i;
final int finalJ = j;
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage(finalI,finalJ);
}
});
}
You can set the JButton object's "name" property and, according to mre's answer, you can call the getSource() method. So you can identity whick button is clicked
Another option is to have your class implement ActionListner (ie, implements ActionListner).
Then when you cycle through your buttons in your loop, you can just say
ledBtns[i][j].addActionListener(this).
Of course, then you have to figure out which object was the source of the event (usually by using if...else blocks). Now that could get unwieldy for 64 objects, but for lesser items, it isn't usually a problem.
Or, you could have the actionPerformed method call change image and pass in the button object, etc to do your work on.
What I've suggested is just another option. I'd do what makes the most sense for your code and is the cleanest and most readable.
I have a list that is poulated via a local text file. I have the following code that simple prints the selected item/items when the button is click.
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
int[] selection = jList3.getSelectedIndices();
// selection.toString();
for (int i = 0; i < selection.length; i++){
Object selString = jList3.getModel().getElementAt(selection[i]);
System.out.println(selString);
}
}
Instead of printing the item I would like each button click on each object to be recorded somehow. I have no idea what kind of component, method etc to implement. Any guidance is appreciated.
My end result will be something similar to this.
System.out.println(SelString has been clicked X amount of times);
Use a hash map with your objects (selString) as keys, and a counter as value. Something like:
private Map<Object, Integer> buttonMap = new HashMap<Object, Integer>
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
Integer counter = null;
int[] selection = jList3.getSelectedIndices();
for (int i = 0; i < selection.length; i++){
Object selString = jList3.getModel().getElementAt(selection[i]);
counter = buttonMap.get(selString);
if(counter == null) {
buttonMap.put(selString, new Integer(0));
}
buttonMap.put(selString, new Integer(counter.intValue() + 1));
System.out.println(selString + " has been clicked " + buttonMap.get(selString) + " times.");
}
}
You can use PropertyChangeSupport to notify each time jList items are clicked, besides you should create a listener to receive events notification (through propertyChangeSupport.addPropertyChangeListener).
Once there, you can get the event properties such as the property name and property's new value which will be selected item on jList3 in this case, for counting how many times some item was clicked, you could use a HashMap, setting the key as the item index of jList and the associated value how many time the item has been clicked:
PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
HashMap<Integer, Integer> clickCounter = new HashMap<Integer, Integer>();
public NewJFrame() {
initComponents();
propertyChangeSupport.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("selectedIndex")) {
System.out.println("Selected index: " + evt.getNewValue());
System.out.println("Selected text: " + jList3.getModel().getElementAt(evt.getNewValue()));
if (clickCounter.containsKey((Integer) evt.getNewValue())) {
clickCounter.put((Integer) evt.getNewValue(), clickCounter.get((Integer) evt.getNewValue()) + 1);
} else {
clickCounter.put((Integer) evt.getNewValue(), 1);
}
}
}
});
}
private void jList1MouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
propertyChangeSupport.firePropertyChange("selectedIndex", -1, jList3.getSelectedIndex());
}
At any time you can retrive how many times certian item was clicked accessing clickCounter
I would suggest using an inner class which holds whatever object you are currently putting into the JList and adding a counter member variable as well as overriding the toString().
class MyListItem
{
int selectionCount;
Object listItem; //may consider generics here, but left them out cause they can be tricky
public MyListItem(Object item)
{
selectionCount=0;
listItem=item;
}
public void incrementSelectionCount()
{
selectionCount++;
}
public String toString()
{
return listItem.toString() + " has been clicked " + selectionCount + " times.");
}
}
Then in your action listener
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt)
{
int[] selection = jList3.getSelectedIndices();
for (int selectedIndex : selection)
{
Object selString = jList3.getModel().getElementAt(selectedIndex);
if(selString instanceOf MyListItem)
{
MyListItem selItem = (MyListItem) selString;
selItem.incrementSelectionCount();
System.out.println(selString);
}
}
}
This should save time on look ups, boxing, etc. Also, this helps keep things sane as the project grows since MyListItem can be grown to deal with all types of actions you may want in the future in case you want different things to happen for things other than button presses. The basic idea here is that the MyListItem should keep track of everything you are interested in tracking so you don't need multiple lists and even worse, to remember to add an item to both a JList and a HashMap or any other data structure. This way it's either on every data structure it needs to be or not at all.