Array of buttons, make only one button change its text when clicked - java

I have an array I fill with buttons and I want an individual button to change its text when clicked.
for (int i = 0; i<4; i++)
{
button[i] = new JButton ("Add");
button[i].addActionListener(this);
box[i] = new JComboBox();
foodOptions.add(box[i]);
foodOptions.add(button[i]);
}
public void actionPerformed (ActionEvent e)
{
button[this].setText("I've been clicked!");
}
The current doesn't work because of incompatible types, what format is appropriate?

Yes, it makes no sense to pass an object, this, into an array index which expects an int, not your GUI object, so I'm not sure what you were trying to achieve with this.
Just get a reference to the JButton that's been clicked from the ActionEvent's getSource() method:
JButton btn = (JButton)e.getSource();
btn.setText("I've been clicked");
Edit:
Also you should avoid using this as your ActionListener since this means that you're likely having your GUI class implement an ActionListener which is asking the poor class to be too many things, to do too much. You're much better off either using anonymous inner classes or else even better use AbstractActions.

Related

Changing ActionListener to append StringBuilder

Within two statements that have drastically shortened my code I need to add a statement that adds the text of a JButton to a StringBuilder. The ActionListener statement exists to disable the JButtons when clicked (a nice aesthetic), but I want to include if possible the ability to append the StringBuilder within the ActionListener as well. The following is the two parts of this code.
theModel.randomLetters();
ActionListener disableButton = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (!(event.getSource() instanceof JButton)) {
return;
}
theModel.currentWord.append((JButton)event.getSource());
((JButton)event.getSource()).setEnabled(false);
}
};
for (int i = 0; i < 16; i++) {
JButton dice = new JButton(theModel.letters.get(i));
dice.addActionListener(disableButton);
boggleGrid.add(dice);
}
The .addActionListener(disableButton) adds the above ActionListener to each button when it is produced by the for loop. However,
theModel.currentWord.append((JButton)event.getSource());
is what I thought would properly append the StringBuilder "currentWord" with whatever value the clicked button holds (hence "((JButton)event.getSource())"). There are no errors per say but I have written separate lines of code in my main class to test whether there are any changes to the StringBuilder when any buttons are clicked. There isn't.
Where and what do I need to do to properly add the value of the clicked JButton to currentWord?
Using (JButton)event.getSource() will cause the StringBuilder to invoke the objects toString method. This isn't what you want, instead, either use the JButton's text property or the ActionEvent's actionCommand property, for example...
theModel.currentWord.append(((JButton)event.getSource()).getText());
or
theModel.currentWord.append(event.getActionCommand());
instead
Unless you specify the JButton's actionCommand yourself, it will use the buttons text as the actionCommand

how to identify a button in a group of loop generated buttons?

I have a group of loop generated buttons made with this code
this.panelCuerpo.setLayout(new GridLayout(4,5));
for(int i = 1; i<=20; i++){
final JToggleButton b = new JToggleButton(new ImageIcon("/images/available.png"));
panelCuerpo.add(b);
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/available1.png")));
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt){
if(b.isSelected()){
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/busy1.png")));
cantidadBoletas++;
}else{
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/available1.png")));
cantidadBoletas--;
}
System.out.println(cantidadBoletas);
}
});
}
The problem here is that I can't use setText() to compare later cause there's no property to hide that text. How can I compare it later?
PS. Each button has a consecutive number, it's easy to assign that number. The real problem lies in where to put it.
You could:
Use the Action API, which lets you trigger the selected state of the associated button. This allows you to de-couple the button from the underlying "action" it should take. Take a look at How to Use ActionsHow to Use Actions for more details
Use the actionCommand property of the JButton. This allows you to have some kind of "identifier" associated with the button which is independent of the text
Use an array or List to maintain a reference to the buttons
You can maintain a List<JToggleButton> of JToggleButton and fetch element later by the index. Apart from that instead of adding ActionListener in loop you can implement ActionListener which can be used for all buttons and you just need to write b.addActionListener(this); in loop.
NOTE : better to start from i = 0 instead of 1

Handling events for a collection of JButtons

I'm attempting to create dynamic interface that essentially creates a grid of a growing number of panels that look similar to the picture below. I've got some rough code to achieve that. I have an issue in regards to handling events for the JButtons that i've added to an ArrayList. I'm aware that creating inner class event handlers is best practise, how would I handle events for a scaling number of buttons that are stored in an ArrayList?
Currently i've resulted to getting the objects source to achieve this.
Global Variables:
ArrayList<JButton> buttons = new ArrayList<JButton>();
Adding buttons to the panel:
for (int i = 0; i < 2; i ++) {
int xTotal = 150;
if (i == 0) {
xTotal = 132;
}
else {
xTotal = 308;
}
xTotal = xTotal + xTotal;
JButton currentButton = new JButton("+");
currentButton.setBounds(xTotal , 375, 45, 25);
currentButton.setFont(currentButton.getFont().deriveFont(14f));
currentButton.addActionListener(new AddHandler());
buttons.add(currentButton);
panel.add(currentButton);
}
Event handler:
class AddHandler implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == buttons.get(0)) {
System.out.println("hello");
}
else {
System.out.println("it worked");
}
}
}
It looks to me like you're creating more than just a collection of JButtons. Instead you appear to be wanting to create (and correct me if I'm wrong), a collection of images/text with JButton. This suggests that you want to create a class that encapsulates one (?) menu item, that holds a JLabel for the image, a JLabel perhaps for the text, perhaps other components, and a JButton that the user can press to select the menu item. This component would be a JPanel that (important here) uses layout managers to intelligently arrange all of its constituent components. I would recommend not using ActionListeners but rather create a separate stand along class that extends AbstractAction, that you can use to set each JButton's Action, either via the JButton's constructor or via its setAction(...) method. Then you could place a collection of these JPanels in a master JPanel that uses perhaps a GridLayout, and have it held in a JScrollPane.
The details of my suggestions and the code would of course depend on the details of your GUI and your current code.
Other "side" recommendations:
Don't use null layouts and setBounds(...). Often newbie Swing programmers feel that this is the easiest way to create complex GUI's, and in the short term it may be, but in the long term, when it comes time to maintain, upgrade and enhance your GUI, it isn't. Also the GUI's so created may look OK on one platform and screen resolution, but they look terrible on all others. Learn about and use the layout managers.
Maybe you want a collection of Actions or the JButtons, but I'm not sure you need this. If the button's Action knows what to do, then no collection is needed. The Action could have a constructor that passes in references to the name and price of the menu item.
Even though youve already accepted another answer, I want to share what I have on my mind.
The idea of my idea is to store a number (maybe an ID) on the button by creating a subclass of JButton.
(ID could be, perhaps the ID of the food item this button is currently linked to)
class FoodButton extends JButton{
long id;
public FoodButton(String text, long id){
super(text);
this.id = id;
}
//Perhaps more constructors
public long getId(){
return id;
}
}
Then writing a single actionListener that gets the source of the event (even though you seem to think thats bad practice, I think it makes sense in this example), Observe:
ActionListener al = new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
FoodButton btn = (FoodButton) e.getSource();
long id = btn.getId();
//Do something now that you know what button was clicked (id)
}
}
Let me note again that this actionListener is created before your loop, so you only need one, and you'll assign the same one to all your buttons.
Then inside your loop instead of creating a normal JButton, you create a FoodButton:
ActionListener al = new ActionListener(){...};
for (int i = 0; i < 2; i ++) {
//...
//Assigning id 'i' here, but you could pick something else
JButton currentButton = new FoodButton("+", i);
//...
currentButton.addActionListener(al);
buttons.add(currentButton);
panel.add(currentButton);
}
I hope this makes some sense to you.
Also, as someone who used to use null layouts too, once you get accustomed to using layoutmanagers, im sure you will like them.

How do I get input from buttons I created using a loop?

I'm trying to make a simple calculator in Java using Swing, and I've created my buttons the following way:
//Our number keypad
public static JPanel numbers(){
//our panel to return
JPanel panel = new JPanel();
//Create and add 3x4 grid layout to panel
GridLayout gl = new GridLayout(3, 4);
panel.setLayout(gl);
//For creating and adding buttons to panel
for(int i = 0; i < 10; i++){
//Create a new button where the name is the value of i
String name = "" + i + "";
JButton button = new JButton(name);
//add action listener
button.addActionListener(handler);
//Add button to panel
panel.add(button);
}
return panel;
}
My question is how do I reference each specific button in my event handler? I can't think of a way to do this without having to manually create each button rather than using a loop.
Thanks.
In your listener, call event.getSource(), and that will return the button which has been pressed. Get the text of the button, and you have its number.
Or create a different instance of your handler for every button, and pass the value of the button (i) to the constructor of the handler. This last solution is cleaner, IMO, because it doesn't depend on the text of the button. If you replaced the text by an image, for example, the first technique wouldn't work anymore.
You can distinguish created buttons by adding the following inside handler:
String buttonText = ((JButton) e.getSource()).getText();
if (<text1>.equals(buttonText)){
//do the stuff
} else if (<text2>.equals(buttonText)){
//do the stuff
} else {
//do the stuff
}
Method #1: go through the child components of the parent JPanel (quite tedious, has to be rebuilt every time you modify the contents of that JPanel). Make sure they're JButtons by using an if . . instanceof clause.
Method #2: as you create them in that loop, add them to a List (or even better, a Map). I prefer a Map personally as it lets me customise the key for that specific JComponent
i.e.
HashMap<String, JComponent> buttonList = new HashMap<String, JComponent>();
for(. .) {
buttonList.put("nameForEachButton", button);
}
I recommend generating the button name based off of the loop counter. Either use your existing name value, or just set it to "button" + i;
Declare your buttons using an array.
JButton[] button = new JButton[9]; //outside the for loop
for (int i = 0; i < 10; i++) {
//put your code
button[i] = new JButton(name);
//your code
}

Accessing a "nameless" Jbutton in an anonymous class from another anonymous class?

I created 26 JButton in an anonymous actionListener labeled as each letter of the alphabet.
for (int i = 65; i < 91; i++){
final char c = (char)i;
final JButton button = new JButton("" + c);
alphabetPanel.add(button);
button.addActionListener(
new ActionListener () {
public void actionPerformed(ActionEvent e) {
letterGuessed( c );
alphabetPanel.remove(button);
}
});
// set the name of the button
button.setName(c + "");
}
Now I have an anonymous keyListener class, where I would like to disable the button based off of which letter was pressed on the keyboard. So if the user presses A, then the A button is disabled. Is this even possible given my current implementation?
Could you not simply declare an array of 26 JButton objects at class level, so that both listeners can access them? I believe anonymous inner classes can access class variables as well as final variables.
I don't know if you want to disable the button or do you want to remove it? In you code you're calling remove and in your answer you're talking about disabling. You could achieve this by adding a KeyListener to the alphabetPanel. So you could add this just before starting the for-loop:
InputMap iMap = alphabetPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap aMap = alphabetPanel.getActionMap();
and instead of your ActionListener added to the JButton call this:
iMap.put(KeyStroke.getKeyStroke(c), "remove"+c);
aMap.put("remove"+c, new AbstractAction(){
public void actionPerformed(ActionEvent e) {
// if you want to remove the button use the following two lines
alphabetPanel.remove(button);
alphabetPanel.revalidate();
// if you just want to disable the button use the following line
button.setEnabled(false);
}
});
You could also iterate through the components, comparing getText() to the key pressed.
As someone else mentioned, anonymous classes can also access members of the outer class as well as local finals

Categories

Resources