Handling events for a collection of JButtons - java

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.

Related

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

JComboBox is refering to old Frame while removeAllItems()

I tried to figure this out myself but I can't. I'm stuck at a strange problem.
I have a Java Program with multiple classes and forms (I use Intellij and the build in GUI-Creator). When I switch from one Screen to another I just call frame.setVisible(false); at the leafing window and frame.setVisible(true); at the window I want to show next.
On a Button Click I make this:
In Class 1:
if (e.getSource() == umschaltenButton) {
this.mainW.goToMainWindow();
logger.log(Level.INFO, "Switched Back to MainMenu");
frame.setVisible(false);
}
And here is the weird part.
In Class 2:
public void goToMainWindow() {
frame = tvElectronics.drawMainWindow(); // I get a new Frame with new Images and so on
frame.addMouseListener(al);
frame.add(BotomPanel); // in here is the JComboBox
frame.setSize(LENGTH, HEIGHT);
comboBox1.removeAllItems(); // Here it tryes to refere to the old frame before i made frame = tvElectronics.drawMainWindow();
Vector<String[]> content = tvElectronics.getContent();
for (int i = 0; i < tvElectronics.getAnz(); ++i) {
comboBox1.addItem((i + 1) + ". " + content.get(i)[3]);
}
comboBox1.setSelectedIndex(chanel);
frame.setVisible(true);
}
And so it tries to update the old frame from class2 which no longer exists because of the new one I just created. And so I have 2 frames open: one as I want it and one strange old frame form class2.
My problem is that I want bind my JComboBox to a new Frame and update it but it is still connected to the old one and that causes weird problems like jumping back in the function. I mean it is at the last line of goToMainWindow() and then it starts again at the first line.
First off you should avoid swapping JFrames as your program does since this is a very annoying GUI design. Please read The Use of Multiple JFrames, Good/Bad Practice? for more on this.
Next, it's impossible for us to tell what GUI view your JComboBox is associated with.
But having said that, it really shouldn't matter. Instead of doing what you're doing, I would give the display class that holds a JCombBox a public method that you call on the containing display class that clears the contained JComboBox's model or that places items in the model. This way, there will be no ambiguity as to which JComboBox you're referring to, and this way you avoid directly exposing a view's inner components.
As an aside, I try to gear my display or view classes towards creating JPanels, not JFrames as this will give my code much greater flexibility.
For example
// my display class
class Display1 {
private DefaultComboBoxModel<String> myModel = new DefaultComboBoxModel<>();
private JComboBox<String> myCombo = new JComboBox<>(myModel);
public void removeAllComboElements() {
myModel.removeAllElements();
}
public void addElement(String ele) {
myModel.addElement(ele);
}
}
Same for your Display2 class. Then you can call the correct method on the JComboBox that is held by the correct view/display.
This way, when you swap displays, perhaps by using a CardLayout, you can clear the JComboBox in the display that is being shown by calling its own method to clear its own combobox's model.

Connect Four Java Game Project, need help on basic concepts

I am working on a lab, it's a connect four game. I'm having trouble specifically with basic concepts like how classes communicate with each other, how to use private instance variables, how to use an ArrayList, how to compare JLabels or set them as something comparable...
To give a brief breakdown I have four classes GUI, Game, Player, Name
I can create the GUI by using two four loops, the game is a grid with 7 columns of 6 pieces. The pieces are images,
JLabel Piece = new JLabel("images/blank.png");
for example to denote an empty spot.
The GUI is based on a JFrame, single content pane and four panels, one for a header which indicates who is playing and who won, another for the 7 buttons accompanying the 7 rows, the grid itself of the possible places to be played and then a button panel which gives you the option to replay.
I'm lacking in a lot of concepts. For instance, the replay button shouldn't appear until the game has ended.
I don't understand how to use an ArrayList. I tried to use
ArrayList<ArrayList<JLabel>> myList = new ArrayList<ArrayList<JLabel>>();
So when I create the GUI by running two for loops like so
For ( c = 0 ; c<8 ; c++) {
ArrayList<JLabel> column = new ArrayList<JLabel>();
For ( r = 0 ; r<7 ; r++) {
ArrayList<JLabel> row = new ArrayList<JLabel>();
JLabel empty = new JLabel("images/blank.png");
row.add(empty);
}
column.add(row);
}
Even this small step I've already got confused.
I know the two for loops above are not correct specifically the ArrayList.
I don't know how to create the arraylist and then use them.
using something like
column.get().get();
myList.get().get();
to get a specific piece.
I don't know how to pass that to an argument so that for example if I push on button 7 for column 7, and no pieces have been played yet, I can start from the lowest area column 7 row 6 and update that to a played piece, red or yellow for the appropriate player.
This is vague and I doubt I'll get anywhere but I am desperate for help. There isn't much time available from the TA's / Teacher and I believe I am lacking significantly to be able to finish this project.
I understand how it works/what I have to do in words but in terms of applying Java code...
I'd appreciate any help.
OK first off you should use an array of Enums. ArrayLists are intended for lots of items and that can have rapidly changing numbers. Arrays are intended to house data in a grid of some sorts. Since you are using a static board, use arrays! They are also much less memory-intensive. Example:
//Note you should use [column][row] as that is common practice.
States[][] grid = new States[7][6];
//And initialize it:
for(int i = 0; i < grid.length; i++)
for(int o = 0; o < grid[i].length; o++)
grid[i][o] = EMPTY_JLABEL;
Then declare an enum (this is a new class) (NOTE: replace FULL_PLAYER_ONE_JLABEL and FULL_PLAYER_TWO_JLABEL with the JLabels that have the image for each.):
public enum States {
FULL_PLAYER_ONE(FULL_PLAYER_ONE_JLABEL), FULL_PLAYER_TWO(FULL_PLAYER_TWO_JLABEL), EMPTY(EMPTY_JLABEL);
//The image of the appropriate state.
private JLabel label;
//Enum constructors must be private
private States(JLabel label) {
this.label = label;
}
public JLabel getLabel() {
return label;
}
}
In your GUI, have a JButton that is only added to the frame when the game is over. Also add a button to indicate when each column has been clicked by the player.
JButton button = new JButton();
//Initialize JButton and add to frame...
//Anytime before the frame is set to visible:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* Perform tests for for what should happen.
For example test whose turn it is then call a method to add a piece to that column. Then call a checker to see if someone has won. If so, display the replay button, otherwise do nothing (allow other player to move).
*/
}
}

How to change make tabbed panels change on their own every few seconds?

I am currently learning Swing and am trying to create a simple program that stores information about different sports teams.
I have created multiple tabbed panels which all hold various information about each team. I would like to be able to have a button that when press displays each tabbed panel say every 10 seconds or so - sort of a slide show effect.
I have read up on action listeners but have not spent a lot of time on them as of yet so i am having trouble implementing this. I would be very grateful if anyone could maybe help me or just give me a push in the right direction. I have posted a snippet of code that i have atempted but i am at a loss about what to actually put inside the loop to achieve this.
slides.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent actionEvent){
for(int i = 0; i<arrayList.size(); i++)
{
//code that changes the tabbed panels every few seconds.
}
}
});
I have created multiple tabbed panels which all hold various various information about each team.
Rather you should focus on creating a JPanel that can display team stats, and not so much JTabbedPanes. The JPanel can then be displayed in a JTabbedPane if desired.
I would use a CardLayout to swap JPanels, and then a Swing Timer to do the swapping. However if you use a single JPanel to display the stats, then you could even display one single JPanel and simply change the model (Team Stats information) that is displayed in it rather than swap JPanels.
As to what to put in your ActionListener, it will not be a for loop at all, but rather a Swing Timer, and you can read about it here: Swing Timer Tutorial.
e.g.,
slides.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent){
int timerDelay = 10 * 1000; // 10 seconds
new Timer(timerDelay, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent evt){
if (count < maxCount) {
// code to show the team data for the count index
count++;
} else {
((Timer) evt.getSource()).stop(); // stop timer
}
}
}).start();
}
});

How to tell which item fired a mouse listener

HI all,
I'm trying to write a simple star rating component. I'm fairly new to the Java language and I'm not sure if what i want to accomplish can even be done in Java. Is it possible for me to add a JLabel inside an array of JLabel, and each JLabel in the array will have a mouse event listener. Now is it possible to set it up so that when the mouse event fires on say Label[3] that i can get the index value of it?
Here is how I built my Panel
public Rating(Integer max,int size) {
JLabel position = new JLabel[max];
this.setLayout(new FlowLayout());
for(int i=0; i != max;i++){
position[i]=new JLabel(icons.getIcon("star-empty", size));
position[i].setOpaque(true);
position[i].addMouseListener(this);
add(position[i]);
}
}
#Override
public void mouseEntered(MouseEvent e) {
JLabel a= (JLabel) e.getComponent();
//****Have some code in here to tell me where in the position array the event came from????***
int index = ?????
}
Thoughts/Idea/Suggestions please.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
THanks.
Instead of using the same listener for each label like you did:
position[i].addMouseListener(this);
...you can create a special listener class that takes the index number, and allows you to find it later:
position[i].addMouseListener(new RatingMouseListener(i));
Each label will have a separate instance of the listener with a different index value. The code for the inner class would look like something like this:
private class RatingMouseListener extends MouseAdapter {
private final int index;
public RatingMouseListener(int index) {
this.index = index;
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered for rating " + index);
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited for rating " + index);
}
}
Then, you just override any method in MouseAdapter.
Also, like other people said, you might want to use JButtons instead of JLabels because they have better support for action events. You can still give them icons.
You could name each JLabel according to its index using its setName method, then use the MouseEvent's getComponent method to get the originating JLabel back, use getName on it and there's your index. That would be one way, but would involve storing the index information in two places (implicitly in its placement in the array, and explicitly as the label's name), so it's pretty much begging for inconsistency to arise.
You could also search through the array for the JLabel reference you get from getComponent, but that's not so great either, especially for large arrays.
The way I usually do it is:
int i;
for (i = 0; i <max; i++)
if (position[i] == e.getcomponent())
break;
now position[i] is the label you are looking for.
Just know that JButtons can look any way you'd like. They can have ImageIcons and don't even have to look like buttons.
Why is the index important? You know how to get the component, so just loop through the array to get the index.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
How does using a button solve the problem of determining the index? However, I also agree using a button is better than a label and then you would use an ActionListener instead of a MouseListener. You can make the button look like a label by using:
button.setBorderPainted( false );
Now if you use an ActionListener you can use the setActionCommand(...) method to store the index value of the button. Then in the event you use the getActionCommand(...) method.

Categories

Resources