I have many GUI forms with many buttons on each GUI form. I am wanting to detect when a user clicks on any of these buttons.
My question is this: Should I add code individually to each button, or can I cater to each button press by a public method that is called whenever a button is pressed? If I use a public method (that I would rather do), how does the method detect the button that called the function? Do the buttons have to be a public variable?
Currently I am using this code (that works, I am wondering if I can do it better):
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println("You clicked the button");
}});
Should I add code individually to each button, or can I cater to each
button press by a public method that is called whenever a button is
pressed?
To my mind (and sanity), I'd separate the action listeners/handlers. Generally speaking it keeps the individual actions/code simple and to the point.
Each button/menu can have more then one action listener if need be.
I'd also take a look at the Action API
If I use a public method (that I would rather do), how does the method
detect the button that called the function? Do the buttons have to be
a public variable?
If your really keen to follow this method, you can pass the ActionEvent to it, which would contain a reference to the source of the action as well as the action command.
Personally, I wouldn't. These kind of methods are notoriously difficult to manage, maintain, debug and update
Related
It might be confusing for some to answer this but I will try to put my question in the best way. I am working with jdbc and gui. Basically I want to display (in buttons format) the particular data received from my sql database. I could get the data correctly and put it to my array of buttons as their names. In other words, I have an ArrayList of buttons with different names/texts received from my database. Thus i really need to make an arraylist of buttons since data are dynamically populated. My problem is, I am so confused of how am going to create an actionListener to each button. Everytime each button is clicked, it must show the values associated with its name. I don't know how am i supposed to pass at least the names of the buttons to my actionListener method (or action Event Handler). If you find it confusing, here is the code for my buttons.
todayTaskButton.add(new JButton(taskForToday.get(i)));
todayTaskButton.get(i).setPreferredSize(new Dimension(300,75));
todayTaskButton.get(i).setBackground(Color.GRAY);
todayTaskButton.get(i).setFont(new Font("Century Gothic",Font.PLAIN,30));
todayTaskButton.get(i).setForeground(Color.WHITE);
todayTaskButton.get(i).setFocusable(false);
Thank you so much
You don't need to pass the name of the button to the ActionListener. It is automatically detected. You just need to implement the method actionPerformed(ActionEvent) in you class.
Then add the listener to the button :
todayTaskButton.get(i).addActionListener(this);
In your actionPerformed method, you can do:
JButton b = (JButton) e.getSource();
String text = b.getText();
Honestly there are so many ways you might achieve this, the problem is picking the right one for you...
You could...
Create a anonymous class for each button, each time your create them
todayTaskButton.get(i).addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...
}
});
While this can work, it can make the code really messy, you also still need a way to map the action back to the button in some way, which can be done using the actionCommand property or using the source property if you don't mind iterating through the list of available buttons
You could...
Create a purpose build class which implements ActionListener for each button, which possibly takes some kind of reference to the data
todayTaskButton.get(i).addActionListener(new TodayActionListener(taskForToday.get(i)));
This is a little more focused, as you don't really care about the button, as you have the "today" value for the listener, so all the normally repeated code could be isolated into a single class and you would simply pass in the "variable" element
You could...
Take full advantage of the Action API and make individual, self contained actions for each button...
public class TaskAction extends AbstractAction {
public TodayAction(String task) {
putValue(NAME, task);
}
#Override
public void actionPerformed(ActionEvent e) {
// Specific action for task
}
}
Then you could simply use
todayTaskButton.add(new JButton(new TaskAction(taskForToday.get(i))));
While this is similar to the previous option, the Action is a self contained unit of work and has a number of properties which the JButton can use to configure it self. The Action can also be re-used for JMenuItems and key bindings, making it incredibly flexible
Have a closer look at How to Use Actions for more details
So, I will do my best to explain this question...
Basically, I have a GUI whose main window has several buttons on it (probably about 10). I am putting the buttons themselves in an array, but when it comes to handling click events for each button, something different is going to happen depending on which one is clicked.
Instead of doing something like this:
#Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("Button1Text") { /* do stuff */ }
else if(e.getActionCommand().equals("Button2Text") { /* do stuff */ }
else if(e.getActionCommand().equals("Button3Text") { /* do stuff */ }
else if(e.getActionCommand().equals("Button4Text") { /* do stuff */ }
}
Is there a more efficient way to handle each button's response when it gets clicked? The idea is that whenever a button gets clicked, a new window will open to let the user perform various tasks associated with that button. I was thinking of somehow using getActionCommand() in combination with the Class.forName()/newInstance() methods, but I'm not sure if there is another (or easier) way to do something like this.
The ActionListener you've shown is sometimes known as a "switchboard listener", and you're correct to think that it can be improved upon. It is quite rigid code, making it hard to debug and enhance.
Best I think would be to not have the GUI implement ActionListener at all but rather to use unique Actions (i.e., AbstractActions) for each class of button and then plug the appropriate Action into the appropriate button. To plug with minimal coupling, consider using dependency injection a la Spring or Guice.
I'm having a hard time connecting all the dots with GWT's event-listener model. Let's say you have a Button widget, and when the user clicks it, you want text somewhere else on the screen from black to red (just thinking of a super-simple example).
When the user clicks the Button, a button click event gets placed on the Event Bus, which is configured with handlers/listeners that want to be notified when this event happens. How does this tie into Places, PlaceChangeEvents, and the GWT History API?
If someone code provide a super-simple, but functional code example of this "roundtrip" process, from button click, to firing the click event on the bus, to handling the event off the bus, to updating the appropriate place/history objects, and finally, changing the text to red, I think I'd be able to connect many of the currently-missing dots. Thanks in advance!
GWT has many types of events and it is not easy to really understand them all. Some events occur only in logic, others come from the DOM (and can be used either in capture or bubbling), etc.
Just go one step at the time, as you seem to be trying to mix different events together. (a button click and history events are completely independent events). In general, the idea is that many classes provide different kinds of events, and you connect handler for the ones you care about. Your handlers can then call other classes or generate other events. The code that you want is actually pretty simple, this is the only class that you need in your project:
public class Sandbox_gwt implements EntryPoint
{
public void onModuleLoad()
{
final Label label = new Label("I'm red");
label.getElement().getStyle().setBackgroundColor("#FF9999");
Button button = new Button("click me!");
button.addClickHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
label.getElement().getStyle().setBackgroundColor("#99FF99");
label.setText("I'm green");
}
});
RootPanel.get().add(label);
RootPanel.get().add(button);
}
}
You can see the "flow" if you are really interested by debugging this, but there is no need to do a "manual roundtrip" or anything. The history, BTW is a separate class to listen for the forward and back buttons in the browser itself, plus some other stuff, but is not necessary at all to do what you mention.
OK, so if I add an ActionListener to a GUI element, and it's the only element I use that ActionListener with, does it matter which of the following lines (a,b) I use to get the checkbox selected state?
final JCheckBox checkbox = (JCheckBox)this.buildResult.get("cbDebugTick");
checkbox.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent event){
boolean bChecked =
// (a) checkbox.isSelected();
// (b) ((JCheckBox)event.getSource()).isSelected();
model.setPrintDebugOn(bChecked);
}
});
It makes sense to me that if I add the ActionListener object to multiple GUI elements, then I should use (b).
And in (b), is it OK to blindly cast event.getSource() to JCheckBox, since I'm the one who added the action listener, or should I program defensively and do an instanceof check?
note: this question is in the context of event listeners in general; kdgregory has some good points below specifically re: checkboxes which I had neglected to consider.
I'd do neither.
If clicking the checkbox is going to start some action, I'd attach an ItemListener, then just look at the selection state in the ItemEvent.
However, checkboxes don't normally invoke actions, they manage state. So a better approach is to examine all of your checkboxes in response to whatever does kick off the action.
Edit: some commentary about the larger issues that the OP raised.
First, it's important to realize that large parts of Swing represent implementation convenience rather than a coherent behavior model. JCheckBox and JButton have nothing in common other than the fact that clicking within their space is meaningful. However, they both inherit from AbstractButton, which provides implementation details such as the button's label. It also assumes that buttons are "pressed", and that pressing a button will initiate some meaningful behavior (the action). In the case of JCheckbox, however, the button press is not important, the change in state is. That state change is signaled to the ItemListener -- which is also defined on AbstractButton even though state changes are meaningless to other button types (the JavaDoc even says "checkbox").
One of the things that Swing did get right -- if hard to use -- is the idea of that an Action is separate from the control initiating that action. An Action object can be invoked from multiple controls: a menu item, a pushbutton on a dialog, a keystroke, whatever. More important from a design perspective is that it takes you away from the idea of a generic "listener" that tries to figure out what needs to happen. I've seen apps where a single listener receives input from the entire menu system, for example, and then runs through a big if/else chain to figure out which menu item was pressed. Using Actions means you have more classes, but in the long run gives you a more maintainable app.
Finally, from a usability perspective, there's a difference between controls that maintain state, such as JCheckbox and JTextArea, and those that initiate actions, such as JButton and JMenuItem. I have seen a (web) app where clicking on a radio button takes you to a different page. That's bad. Even if you're planning to use listeners internally, to update the state of some model, you should ask yourself why the collection of GUI elements do not in themselves provide you with a model.
For the case where the listener is exclusive (such as an anon listener), I use (a).
If the listener will be reused (eg, this is an instance of ActionListener) I'll write it as:
#Override
public void actionPerformed(ActionEvent event) {
Object src = event.getSource();
if (src == checkbox) {
boolean bChecked = checkbox.isSelected();
// ...
}
}
If you have several checkboxes and they are processed the same way, then instanceof makes sense.
in (b) to be rigourous, you should indeed do a instanceof check, but it's not that important. I would think both these lines are fine and acceptable, though (b) would be "better code"
Although, what is usually done in an action listener is simply call another method customized to your checkbox. So it would look like something like this:
#Override public void actionPerformed(ActionEvent event) {
//your treatment would be in this method, where it would be acceptable to use (a)
onCheckBoxActionPerformed(event)
}
I'd program with b defensively as it's the best-practice option. But if only you are ever going to use the code then there is no reason why you can't do a. However, imagine how happy you will be with yourself if you come back to it at some future point, change something and find you wrote good code which you can directly reuse...
Now, I'm not sure if this is possible or even the best way to accomplish what I'm trying to do, but basically I'm creating a very simple simulation program with a very simple Swing GUI.
After each round of the simulation, some buttons on the interface are enabled for the user to make changes and then the user can press the "continue" button to start the simulation again. The simulation itself is basically a while loop that needs to wait for the user action before continuing. My question is how can I have the program stop and wait until the user presses the "continue" button? Please let me know if there are any more details I can provide if this is unclear!
Edit:
I'm going to add some simplified code here so maybe this will make more sense. The program is in two parts, the simulation class and the view. So the button to be pressed is in the view class while the simulation is happening in its class.
Simulation class:
SimulationView view = new SimulationView(); // extends JFrame
while (!some_condition) {
// code
// need user action via button press here before continuing!
}
Most likely the best way to go is enclose one round of the simulation in a method, that would then be executed from the action listener attached to the button you want.
After edit:
Somebody needs to control the simulation in the first place, so you could do the following:
SimluationClass
{
public int startSim()
{
...
}
}
class SimulationView
{
SimulationClass sim = new SimulationClass();
private void init()
{
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
void actionPerformed(...)
{
sim.startSim()
}
});
}
}
Keep in mind though that this will freeze your gui as the sim method will be executed from the event thread.