I'm quite new to interface design and struggling to figure out what the best way to handle events is. In the straight forward case of the handler and the (in this case) buttons causing the event being in the same class, that's fine, I get it. The handler can see the buttons so that it can say:
if (event.getSource() == myButton)
and also, the handler is in the same class so it can add tabs to an object local to that class or similar.
Problem: I don't know how I should be dealing with the case when the handlers and event generators are in different classes.
e.g.
From my main layout class I create and show a custom dialog. That dialog is implemented in its own class. Ideally dialog would use the handler from the main layout class (it implements ClickHandler), which would be fine, but my application has a few different ClickEvents. I distinguish between them as above by checking the source. In this case the buttons are in the dialog class though, so I can't simply say:
if (event.getSource() == myDialogbutton)
as myDialogButton is not in scope.
Any hints for how this should work would be appreciated.
D
Perhaps I can help you with my solution ...
I inherited ClickHandler to an own class which is generic. You can give the ClickHandler any kind of object you want and will be able to access it from the method within.
Example:
import com.google.gwt.event.dom.client.ClickHandler;
public abstract class ClickHandlerData<T> implements ClickHandler {
private T data;
public ClickHandlerData(T data)
{
this.data = data;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
}
Now, in case of a button:
Button btn = new Button("click me");
btn.addClickHandler(new ClickHandlerData<Button>(btn)) {
public void onClick(ClickEvent event) {
Button btn = getData();
...
}
}
I use this class to pass parameters like Integers or something else to the ClickHandler. For instance:
for (int i=0;i<10;i++)
{
Button btn = new Button("click me");
btn.addClickHandler(new ClickHandlerData<Integer>(i)) {
public void onClick(ClickEvent event) {
Window.alert("you klicked button "+getData());
...
}
}
}
I also do the same for AsyncCallbacks, for Commands, for everything else I need to pass data to.
Hope this helped you a bit.
It appears to me that you are trying to use one listener for multiple buttons, unless several of the buttons have the same function they should have different listeners.
In general you should try to have one listener per function, instead of one listener per "event generator".
If you have for example a logout button, it may have a listener from the LoginStatusWidget (displaying who the client is logged in as) and a listener from an object responsable of notefying the server of the logout.
It will serve to seperate the components from each other.
At first i recommend you to try to collect your Buttons and their ClickHandlers in the same class, but if in your case it is not possible, I have a suggestion to you:
When you are creating your Button you can add some information to them:
Button button = new Button("submit");
button.setLayoutData(someObj);
And then after firing event you can get your Button from event in your ClickHandler and find out which button it is :
Button button = (Button) event.getSource();
MetaData someObj = (MetaData) button.getLayoutData();
Try creating a new listener for each anonymous or serial widget e.g. button in a FlexTable. That way their life cycles are connected and they only refer to each other.
Extend the widget
Give it an id and add it to the constructor [make sure the id is one of a kind]
Implement the listener class.
create a new instance of the listener each time you create an item of the same kind.
I'm guessing there are specific objects connected to the widgets. If so keep a HashMap.
May the force be with you
Can't you just do:
final Button source= new Button("My Button");
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
doSomething(source);
}
}
Note the button instance has to be marked final.
Related
I feel that I'm missing something when it comes to statically typed languages. When I pretty much only used perl way back, there were many ways I could tell an object which function to call.
Now that I'm in Java, I fail to see how I can do something similar in an easy fasion
I have a generic Button class. This is subclassed by all of the actual buttons that will be used: Each with a different method to call when clicked.
Is there really no way of passing a reference to a method to call when clicked, so that I can use one class for all of the buttons?
At present, I create buttons like this:
// Specifically using the subclass that sets "firemode" to "close"
FiremodeClose fc = new FiremodeClose(Settings.ui_panel_start, Settings.ui_panel_row_firemode, game);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);
This ofcourse couses a myriad of subclasses, each one differing only in placement, label/graphics, and method call. It makes more sense to do something similar to this:
// Generic button, the method that sets "firemode" is somehow passed as arguement to the contsructor.
Button fc = new Button(&referenceToFunctionToCallWhenClicked, otherArguementsEtc);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);
Like I said, I feel I must be missing something, because it makes sense that there should be a way of achieving this, thus letting me getting away with just one Button class without any subclasses.
If that's what interfaces are for, then I must've been using them for something else than their intended purpose. I'd love to see an answer involving some code examples for this.
Have your Buttons implement the observer pattern, just like Swing does. Then you can even just use Swing's ActionListener interface, or even Runnable is not a bad choice, or e.g. roll your own:
// Your interface.
public interface MyButtonListener {
public void buttonClicked ();
}
// Somewhere else:
Button fc = ...;
fc.addButtonListener(new MyButtonListener () {
#Override public void buttonClicked () {
// do stuff here
}
});
// And in your Button have it simply iterate through all of its registered
// MyButtonListeners and call their buttonClicked() methods.
There are myriads of other ways to implement this. For example, you could even do something like:
public interface ThingThatCaresAboutButtons {
public void buttonClicked (Button button);
}
Then have your higher level UI logic be something like:
public class MyUI implements ThingThatCaresAboutButtons {
#Override public void buttonClicked (Button button) {
if (button == theOneButton) {
// do whatever
} else if (button == theOtherButton) {
// do whatever
}
}
}
And when creating buttons:
theOneButton = new Button(theUI, ...);
theOtherButton = new Button(theUI, ...);
Or have them maintain a list instead of a single object passed in the constructor. Or whatever.
Endless ways to skin this cat but hopefully you get some inspiration here. Check out how Swing works.
You could for instance use Runnable:
class MyButton {
private final Runnable action;
public MyButton(Runnable action) {
this.action = action;
}
...
}
And then call action.run() when the button is clicked.
Then when creating a button, you can pass a reference to a method, as long as it has the void return type, and takes no arguments.
Button fc = new Button(EnclosingClass::methodToCall, otherArguementsEtc);
Other interfaces can be used for different method signatures.
In Java 8 you can use both method references and lambdas:
class Button {
Button(Runnable function) {
}
}
Button b1 = new Button(() -> System.out.println("works!"));
Button b2 = new Button(System::gc);
You can do similar thing in Java <8, but it's more verbose with anonymous classes:
Button b3 = new Button(new Runnable() {
#Override
public void run() {
System.out.println("works!");
}
});
So i just stumbled upon this problem while coding my program using MVC.
I have a private JButton in the View class. I wrote the method to add the listener to all respective buttons. However, when i'm trying to code the ActionPerformed() part it throws an error about JButton not being visible.
Setting JButton to public solves the problem completly, but is it the right thing to do? Is there another way of setting the ActionListener without making the JButton public?
public class learningView extends JFrame {
private JButton viewButton = new JButton("View Resources");
public void addButtonListener(ActionListener listenerForButtons) {
viewButton.addActionListener(listenerForButtons);
saveButton.addActionListener(listenerForButtons);
addButton.addActionListener(listenerForButtons);
}
}
public class learningController {
private learningModel theModel;
private learningView theView;
public learningController(learningModel theModel, learningView theView) {
this.theModel = theModel;
this.theView = theView;
this.theView.addButtonListener(new buttonListener());
}
class buttonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == theView.viewButton) {// This is where problem arises
}
}
}
}
Hastebin for view and controller classes (without model) for the convienience.
http://www.hastebin.com/ecawolusal.avrasm
Since viewButton has private access to LearningVew, it will simply be inaccessible out of that classes context.
Now, before you change the access level, you might consider changing your approach.
Rather then adding an ActionListener to each button which notifies an external source, it might be simpler to have the view to monitor the buttons itself.
Before you get up in arms over how this would break the MVC, the idea would be to then have the view raise a simpler, more dedicated event for the button in question, for example, viewButton could raise viewWasActivated or something, to which the controller would then respond.
This would require you to define a interface contract for both the view and the controller so they knew what information they were capable of passing to each other and what events might be triggered. This protects the view controls and means you don't need to expose the unnecessarily.
Which is demonstrated in more detail here.
The other choice would be to use the actionCommand property of the buttons instead of comparing references of the buttons to the event source, but you would first need to check that the source of the action event was a button ... and I personally don't like "bulk" ActionListeners, they get messy real quickly...
I'm writing a simple UI just to get the hang of things. I have a tabbed window with two tabs, one has a button that counts up an integer, the other has a text field showing the content of said integer. Or at least that's the plan.
Everything works just fine if I stuff everything into one class. I can access tab 1 from my actionlistener and change the text field in tab 1 from the button press in tab 2. But I don't want my entire program to be in one class, obviously.
And here I have no idea what to do: I need to tell the textfield in the Class Tab1 to change on the button press in the Class Tab2. What's the right thing to do here? My first thought was to hand over an instance of Tab1 in the creation of Tab2, so I could do tab1.changeText(). But that would get messy quickly once I'd get more tabs that interact with each other. So, instead, I want to update the content of the first tab every time it is opened, but I don't know how to do that. And I don't know if that's the right thing to do, either. So, help!
Here's some code. "content" is an instance of Content, a class handling all the logic like adding to the counter.
Main GUI Class:
public class GUI extends JFrame {
//Stuff..
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("One", new Tab1(content));
tabs.addTab("Two", new Tab2(content));
//Stuff..
Tab 1:
public class Tab1 extends JPanel {
public Tab1(Content content) {
JPanel tab1 = new JPanel();
//Stuff..
JTextField tfCount = new JTextField(content.getCounter(), 10);
tab1.add(tfCount);
this.add(tab1);
//Stuff..
Tab 2:
public class Tab2 extends JPanel {
public Tab2(Content content) {
JPanel tab2 = new JPanel();
//Stuff..
JButton btnCount2 = new JButton("Count");
btnCount2.addActionListener(new TestListener(this.content));
tab2.add(btnCount2);
this.add(tab2);
}
private class TestListener implements ActionListener {
Content content;
public TestListener(Content content) {
this.content = content;
}
#Override
public void actionPerformed(ActionEvent e) {
this.content.addToCounter(1);
}
}
Now, if all of that would be in one class (plus subclasses), I could just access tfCount from Tab2 and do tfCount.setText(content.getCounter());. Now tfCount is in a different class, though, and I cannot access it, unless I hand over an instance of Tab1 to Tab2 (like tabs.addTab("Two", new Tab2(content, Tab1);). Couldn't I instead get Tab1 to repaint itself whenever it is opened, like having a method that executes tfCount.setText(content.getCounter()) in Tab1 whenever it is opened, or something along those lines? If so, how do I do that?
With you controls separated in this manner you have a view choices...
You Could...
Share an instance of each "tab" with each of the other tabs, allowing them to either access the others controls or attach listeners across each other. This is very tightly coupled and messy.
The other problem is, does the button really care about the text field or visa versa...
You Could...
Create a simple model that contains the current int value and provides a means to change that value.
The model would have the capacity to fire a ChangeEvent (for example) when the value is changed, which interested parties could listen for and update themselves accordingly.
This decouples the code, reducing the complexity and greatly increasing the flexibility and reuse of various elements of your code.
This is commonly known as an observer pattern and is widely used in Swing.
A possible (listener) example...
For me, I always start with an interface, this describes the absolute minimum requirements that must be meet in order to achieve the required goal. Each tab will want to know the current value, be able to set the next value and listener for changes to the model...
public interface NumberModel {
public int getValue();
public void setValue(int value);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
An abstract implementation deals with the more "common" implementation details, things that a concrete implementation won't want to have to implement, as it's common enough to all implementations. In this case, that would the listener management...
public abstract class AbstractNumberModel implements NumberModel {
private List<ChangeListener> listeners;
public AbstractNumberModel() {
listeners = new ArrayList<>(25);
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected ChangeListener[] getChangeListeners() {
// FIFO...
List<ChangeListener> copy = new ArrayList<>(listeners);
Collections.reverse(copy);
return copy.toArray(copy.toArray(new ChangeListener[listeners.size()]));
}
protected void fireStateChanged() {
ChangeListener[] listeners = getChangeListeners();
if (listeners != null && listeners.length > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
}
And finally, a concrete implementation, which deals with the implementation specific details...
public class DefaultNumberModel extends AbstractNumberModel {
private int value;
public DefaultNumberModel() {
}
public DefaultNumberModel(int value) {
setValue(value);
}
#Override
public int getValue() {
return value;
}
#Override
public void setValue(int num) {
if (num != value) {
value = num;
fireStateChanged();
}
}
}
We could be a slightly more flexible model by doing something like public interface NumberModel<N extends Number> which would allow you define models that could hold Integer, Double, Float and Long for example, but I'll leave that to you.
Each of you tab views will need a setModel(NumberModel) method, so you can pass the model it. In these methods, you will attach a listener to the model and get the current value so that the model and view are in sync.
I am building a simple app and I am implementing it in a simple MVC pattern where the controller adds event handlers to the view. Here's a sample controller code attaching a handler to the UI.
Basically, the code adds an event handler when the UI's save button is clicked. The UI contains the name and id number entry. What I wanted to happen is to pass the name and id number into the actionPerformed function.
ui.onAddStudent(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.print("test");
}
});
And the receiving function in the UI (in another file) is the following.
public void onAddStudent(ActionListener handler){
//something missing here
addStudent.addActionListener(handler);
}
I am not really into Java because it's not my forte. I actually do JavaScript. Now, a similar handler In JavaScript, one can use the call() or apply() method to call the handler and pass in additional parameters. If the above code was in JS, it would be like
//in the controller
ui.onAddStudent(function(event,id,name){
//I can use id and name
});
//in the UI
ui.onAddStudent = function(handler){
//store into a cache
//add handler to the button
}
//when student is added (button clicked)
handler.call(this,event,id,name);
How do I do the same thing in Java?
You have two choices:
let it as it is, and have the controller get the ID and name from the GUI (and that is the easiest and simplest solution, IMHO)
use your own Event and Listener types, containing this information. For example:
public class StudentAddedEvent {
private long ID;
private String name;
...
}
public interface StudentAddedListener {
void studentAdded(StudentAddedEvent event);
}
The UI would register an ActionListener on the button, and this action listener would do:
#Override
public void actionPerformed(ActionEvent e) {
long id = getIdInGui();
String name = getNameInGui();
StudentAddedEvent event = new StudentAddedEvent(id, name);
for (StudentAddedListener listener : studentAddedListeners) {
listener.studentAdded(event);
}
}
You can define your own Actions too, and set those to the buttons (constructor argument or setAction) and other components.
Extend AbstractAction for that.
im sitting on this for 4 hours now, and once again I end up on Stackoverflow because I just cant solve this (simple) problem.
I want to fire a method when I click a button, Google gives an Example like this:
// Listen for mouse events on the Add button.
addStockButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
addStock();
}
});
But this creates a new Instance(?..How can they even create an instance of Clickhandler, since its an Interface) everytime the button is clicked. How can I solve this that all buttons share a Clickhandler and the Handler askes the Button which button he is, so he can fire the method attached to that button.
Any Ideas? If you this is to vage information and you require more code please let me know.
Thanks in advance,
Daniel
Java creates a new instance of an anonymous class that implements ClickHandler. Which it can do because you provide an implementation for the onClick function specified by the interface.
This class is however not created when you click on the button but at the moment you call addClickhandler. If you need the handler for multiple events do something like:
ClickHandler handler = new ClickHandler() {
public void onClick(ClickEvent event) {
addStock();
}
};
addStockButton.addClickHandler(handler);
someOtherButton.addClickHandler(handler);
Within the handler you can identify from where the event is coming using event.getSource().
If you have access to your button variables you could simply check the pointer
if (addStockButton == event.getSource()) ...
Or you can cast the result of getSource to the appropriate type and access the properties/methods of the object.
Eelke has already answered your question. I just add that if you would use GWT's UiBinder feature, you could achieve what you want like this:
#UiField
Button addStockButton;
#UiField
Button removeStockButton;
#UiHandler({ "addStockButton", "removeStockButton" })
void handleClickEvents(ClickEvent event)
{
if (event.getSource() == addStockButton)
{
addStock();
}
else if (event.getSource() == removeStockButton)
{
removeStock();
}
}
Its an anonymous instance of the interface, this is like declaring a new class that implements that interface.
I would have to ask why you would want to do this, you would need to make the ClickHandler contain a reference to its parent. You would also need to make the buttons identifiable so you can select the right one in the body of the ClickHandler. Is your need to only have a single instance really that bad that you can't have multiple anonymous instances ?