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!");
}
});
Related
I have two buttons in my project that both have a "+" label. When the actionPerformed() method is called, it calls a specific method based on the label. How can I distiguish between two JButtons with the same label? Is there a better way to do this then how I've done it?
This is the definition of the buttons:
JButton keypadPlus1 = new JButton(" + ");
JButton keypadMinus1 = new JButton(" - ");
JButton keypadPlus2 = new JButton("+");
JButton keypadMinus2 = new JButton("-");
Adding the ActionListeners for the buttons:
keypadPlus1.addActionListener(backEnd);
keypadPlus2.addActionListener(backEnd);
keypadMinus1.addActionListener(backEnd);
keypadMinus2.addActionListener(backEnd);
The actionPerformed #Override in the backEnd:
public void actionPerformed (ActionEvent event) {
String command = event.getActionCommand();
if (command.equals("+")) {
calcLifePoints(command);
}
if (command.equals("-")) {
calcLifePoints(command);
}
if (command.equals(" + ")) {
calcLifePoints(command);
}
if (command.equals(" - ")) {
calcLifePoints(command);
}
}
You could...
Use ActionEvent#getSource
You could...
Set the actionCommand property of each button to something unique and use ActionEvent#getActionCommand
You could...
Use separate listeners, either anonymously or as inner or outer classes depending on your needs
You could...
Make use of the Action API, which would allow you to define a common/abstract Action which defined the common properties (like the + text) and then extend this to make unique actions for each button
See How to Use Actions for more details
You could...
Use JButton#putClientProperty to set some unique flag on each button and cast the ActionEvent to a JComponent and use getClientProperty to retrieve the flag ... but given the previous suggestions, I'm not sure why you'd bother
You shouldn't have a single listener handle the behavior for different responsibilities. If the two + buttons do not do the same thing, give the buttons separate listeners.
This will allow your code to be a lot more cohesive. By reducing your listeners to 1 responsibility each, you'll be able to re-use those responsibilities. It also make testing easier, allowing you to test each behavior in complete isolation.
Although if you must, ActionEvent#getSource() returns which ever component triggered the event. Doing a reference comparison will allow you to determine which object triggered the event.
The best way to handle this would to separate the responsibilities your current listener has into separate classes:
class FirstListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
}
}
Lets assume FirstListener represents your first + button behavior. If that behavior requires any external objects (such as a field in a different class), simply pass it through the constructor:
class FirstListener implements ActionListener {
private Object dependency;
public FirstListener(Object dependency) {
this.dependency = dependency;
}
//actionPerformed declaration
}
You can do the same for the other buttons (for example, the second + button).
If you feel this is a bit excessive, feel free to use lambda expressions to declare the listeners:
//Java 8+
button.addActionListener(event -> {
});
This doesn't give you the same modularity as the previous example, as the behavior is no longer separated from the actual class: you will be forced to change the implementation to change the behavior, rather than using dependency inversion to simply pass a different object which also implements ActionListener.
Instead of this,
public void actionPerformed (ActionEvent event) {
String command = event.getActionCommand();
if (command.equals("+")) {
calcLifePoints(command);
}
if (command.equals("-")) {
calcLifePoints(command);
}
if (command.equals(" + ")) {
calcLifePoints(command);
}
if (command.equals(" - ")) {
calcLifePoints(command);
}
}
Use like this,
public void actionPerformed (ActionEvent event) {
Object command = event.getSource();
if (command.equals(keypadPlus1)) {
calcLifePoints(event.getActionCommand());
}
if (command.equals(keypadMinus1)) {
calcLifePoints(event.getActionCommand());
}
if (command.equals(keypadPlus2)) {
calcLifePoints(event.getActionCommand());
}
if (command.equals(keypadMinus2)) {
calcLifePoints(event.getActionCommand());
}
}
I want to find out whether method for some object is being called for that instance or not.
Is it possible in java ?
Like ...
class Button {
public void focus(){}
public void setName(){}
}
class MyTest {
public static void main(String[] args){
Button button = new Button();
button.focus();
// I want to find out on button instance whether focus() or setName() is called or not.
whetherMethodCalled(button);
// OR
whetherMethodCalled(button, 'focus');
whetherMethodCalled(button, 'setName');
}
}
EDIT : Forgot to add Button class is third party class which I cannot modify... Also I want to check in my code whether method has called for given object instance or not on basis of that I have to write some code.
In order to reduce extra work, perhaps profiling your application with JConsole or another tool is good enough to show if certain methods have run. Another option is using a code coverage tool like EMMA which detects dead code. There is a list of open-source profilers for Java at http://java-source.net/open-source/profilers and EMMA is at http://emma.sourceforge.net/.
With some extra work AspectJ could be use to intercept method calls without changing existing code. For example, the following would intercept calls to Button.focus()
#Aspect
public class InterceptButtonMethods {
#Before("execution(* Button.focus())")
public void beforeInvoke() {
System.out.println("Button.focus invoked");
incrementFocusCount();
}
}
If more extra work is ok, there is a way to wrap all calls to the Button's focus() and setName() methods so that they update separate counters in addition to their normal functions. This can be done by extending Button in YourButton class which is identical to Button except for a couple of int counters with getters, setters and increment methods; and countingFocus() and countingSetName() methods which update their counters and call focus() and setName() respectively, such as in outline:
Class YourButton extends Button {
int focusCount;
int setNameCount
int getFocusCount() {return this.focusCount;}
void setFocusCount(int counter) {this.focusCount = counter} // optional to reset counter
void incrementFocusCount() {this.focusCount = getFocusCount() + 1;)
...
void countingFocus() {
incrementFocusCount();
focus()
}
...
}
If it is required in many places and involves complex things, I recommend to use Mockito to test your code. Using that you can verify if the method was invoked (also how many times if invoked)
You can mock the button and verify in your MyTest how many times the method must be called. Using Mockito you can mock and stub your methods(Stubbing voids requires different approach from when(Object) because the compiler does not like void methods inside brackets) and then verify it using verify statement.
verify(mockButton, times(1)).focus();
verify(mockButton, times(1)).setName();
You can write a wrapper class over the 3rd party Button class through which all calls to Button class will be made.
This wrapper class can keep track of whether each method has been called or not
class ButtonCaller {
private Button button = null;
private boolean focusCalled;
private boolean setNameCalled;
public ButtonCaller() {
button = new Button();
focusCalled = false;
setNameCalled = false;
}
public void focus() {
button.focus();
focusCalled = true;
}
public void setName() {
button.setName();
setNameCalled = true;
}
public void whetherMethodCalled(ButtonMethod method) {
switch (method) {
case FOCUS:
return focusCalled;
case SET_NAME:
return setNameCalled;
}
throw new RuntimeException("Unknown ButtonMethod !!!");
}
public static Enum ButtonMethod {
FOCUS,
SET_NAME;
}
}
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.
I can't get my head round this one. I've tried to adhere to the MVC pattern for the first time and now have difficulties accessing the source of an ActionEvent because the ActionListener is located in a different class. But let the code do the talking...
In the "view":
// ControlForms.java
...
private JPanel createSearchPanel() throws SQLException {
...
comboBoxCode = new JComboBox(); // Field comboBoxCode -> JComboBox()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
AutoCompleteSupport<Object> support = AutoCompleteSupport.install(
comboBoxCode, GlazedLists.eventListOf(jnlCodeArray));
}
}); // Auto-Complete comboBox from GlazedLists
...
public void setComboListener(ComboListener comboListener) {
comboBoxCode.addActionListener(comboListener);
}
...
}
Then, in what I term the controller, I have two different classes:
// Controller.java
public MyController() throws SQLException {
...
addListeners();
}
...
private void addListeners(){
View view = getView();
getView().getControlForm().setComboListener(new ComboListener());
}
and
public class ComboListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("ComboBox listened to! e = " + e.toString());
}
}
Now, e obviously doesn't give the name of the variable (which at the moment I wish it would), so I cannot if test for e.getSource().
My question is thus: is there either a) a way to query (via if for example) the source of e, or b) a less complicated way to get to the variable name?
Many, many thanks in advance for your insights and tips!
Why do you need the name of the variable? Why can't you do the event handling like this
public class ComboListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
JComboBox source = (JComboBox)e.getSource();
//do processing here
}
}
I'd think that if you need to do processing according the variable name, obviously you need different listeners for different combo boxes.
Generally, there are only two situations in which you should use a listener like that: a) you're going to handle a certain event the same way for a bunch of objects, or b) you're only going to use the listener for one object. In the latter case, I'd prefer handling the event locally anyway.
That said, the direct answer to your question is: you shouldn't have to check inside your ActionListener implementation to see whether the appropriate object is the source of the event; you should simply only add the ActionListener to that one object.
One final note: without knowing the specifics of your architecture... generally, MVC will treat all event handling as part of the View (it reduces coupling) and the View will pass commands or method calls or your own events (i.e., not Swing's) to the Controller.
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 ?