Multiple JButtons, one Eventlistener in Java - java

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.

Related

Moving and organizing Java codes on another files

I would like to create a new file and put this block of action buttons codes to be organized on the other files i dont know how i would be able to move it.
Iam making this code and its a little bit disorganized and im having a hard time on what is their purpose.
Is there any other easier method to create a actionlistener codes?
public void buttonAction (){
bgButton[0].addActionListener(e -> {
bgPanel[0].setVisible(false);
bgPanel[0].remove(bgButton[0]);
bgPanel[1].setVisible(true);
});
for (int a = 3,c=0 ; a <12; a++, c++){
final int b=c;
final int d=a;
bgButton[a].addActionListener(e -> {
if (input>=0&&input <=9&&Num[b]!=0){
input = Num[b]*10;
if(input!=0)
createObject(1,12,283,245,85,61,numFile[b]);//12
bgButton[d].setEnabled(false);
Number[b]=-1;
} else if (input >9&&input<100&&input%10==0&&Num[b]!=0&&buttonClicked){
input += Num[b];
buttonClicked = !buttonClicked;
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
Number[b]=-1;
}else if (Num[b]==0&&buttonClicked){
input += Num[b];
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
buttonClicked = !buttonClicked;
Number[b]=-1;
}
System.err.println("total " + input);
bgPanel[1].revalidate();
bgPanel[1].repaint();
});
}
bgButton[14].addActionListener(e -> {
for (int c =0 ,a=3; c <9; a++,c++){
final int b = a;
final int d=c;
int firstNum = input/10;
int secondNum=input%10;
if (Number[c]==-1&&bgButton[13]!=null){
attack = input;
generateSoloNum(d);
bgButton[b].setEnabled(true);
updateButtonIcon(b,64,48,numFile[d]);
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
Number [c]=0;
input =0;
}
}
buttonClicked = true;
bgButton[13]=null;
});
createObject(1,15,149,244,50,38,"res/imageAssets/x.png");//15
bgButton[15].addActionListener(e -> {
input = 0;
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
buttonClicked = true;
for (int a = 3,c=0; a<12;a++,c++){
bgButton[a].setEnabled(true);
Number [c]=0;
}
});
}
I tried import Main.UI; and import Main.Action;
Action is the new file that i want to move it into.
tried using chat gpt but it makes no sense
All methods in Java need to be associated with a class. You cannot move methods from listeners into separate files.
You can create separate listener implementations that include these action methods. If you refactor that way you'll give instances of those listener implementations to your Swing frame and call them instead of keeping them in one big Swing class.
I think this is a good idea. You would change implementations by injecting new classes instead of modifying the frame code.
Each of those addActionListener lambdas would become part of separate classes.
Swing UI code tends to turn into walls of code if you're not careful. I have never seen a well decomposed Swing UI application posted here.

"variable referenced from inner class must be final "problem

I was creating a code having 36 buttons in Jbutton array buttons[].
I added an action listener to each of those using a loop
for (int ghe = 0; ghe < button.length; ghe++) {
button[ghe].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
button[ghe].setVisible(false);
}
});
}
I want the clicked button to be made invisible but every time the netbeans ide gives a syntax error
variable referenced from inner class must be final
What should I do?
As your method is specified, it will not work. ghe is of type int, which does not provide a .setVisible method.
My solution is to save the button from the array into a variable and work with it.
for (int ghe = 0; ghe < button.length; ghe++)
{
JButton currentButton = button[ghe];
currentButton.addActionListener(e -> currentButton.setVisible(false));
}

find JButton source in another ArrayList

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.

How to pass a value from a button to another class

Scenario: I have a series of jbuttons (created at runtime) and each of them has a number in his label. Buttons are created with this code:
for (int i = 1; i <= tablesNumber; i++) {
JButton button = new JButton(Integer.toString(i));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Orders().setVisible(true);
}
});
jPanel1.add(button);
}
I need to pass to the class Orders the number of the button which fires the event, e.g. if the user clicks on button number 5 I need to pass the value 5 to Orders.
How can I do this?
Thanks.
From your question:
pass to the class Orders the number of the button which fires the event
You could just capture the loop iteration variable i so it can be used inside your anonymous event handler. For the sake of argument I have assumed you want to pass the number into the constructor, but you can use it however you like:
for (int i = 1; i <= tablesNumber; i++) {
final int t = i; // <-- NEW LINE HERE
JButton button = new JButton(Integer.toString(i));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Orders(t).setVisible(true); // <-- USE t here however you need to
}
});
jPanel1.add(button);
}
Without final int t = i you may get the compiler error "Cannot refer to a non-final variable i inside an inner class defined in a different method". This is because a capture variable (i.e. a variable from an outer scope used inside an anonymous class' method must be final (or effectively final - this behaviour has changed slightly as of SE 8).

How can I increment a variable on button click?

I'm trying to make a small game like thing for school. It's designed to help you learn your times tables. What I want is for the multiplier of the table to be random each time (5x8 then 5x3 then 5x9 etc).
I've got the generating of the numbers in control with an array as can be seen below
public static Integer[] generateNumbers()
{
Integer[] arr = new Integer[12];
for(int j = 0; j < arr.length; j++)
{
arr[j] = j+1;
}
Collections.shuffle(Arrays.asList(arr));
System.out.println(Arrays.asList(arr));
return arr;
}
How can I make it so that every time the user clicks a button, the next number in the array is selected, baring in mind that the button is declared in another class, and the ActionListener is also declared elsewhere?
Oh and the array is available class-wide as the function is declared like this:
public static Integer[] arr = generateNumbers();
Thematic answer
public class UnicornFrame extends JFrame {
private Integer[] poneyArr = MyClassThatGeneratesNumbers.generateNumbers();
private int poneyCounter = 0;
private JButton poneyButton;
public void poneyInit() {
System.out.println("Unicorns are poney magical friends!");
poneyButton = new JButton("OMG! Ponies!");
// Java 8 Lambdas! Yey!
poneyButton.addActionListener(e -> {
if (poneyCounter >= poneyArr.length) {
poneyArray = MyClassThatGeneratesNumbers.generateNumbers();
poneyCounter = 0;
}
Integer selected = poneyArr[poneyCounter++];
System.out.println("OMG! I have selected " + selected);
});
// other stuff
add(poneyButton, BorderLayout.CENTER);
}
}
The button int he separate class is not needed, the action listener will communicate with your array whenever its clicked, the easiest way I see is to put a public method int the array's class, that takes an index, which then increments the index, and returns the element stored at it.
Have fun coding, but these assignments are meant for you to scratch your head, try writing some code, and let us know if it breaks, rather than asking for general answers.

Categories