This is a short lab whose aim is to provide a detailed explanation of event handling via a simple standalone non-android application
Here is an extract from ResidenceActivity.java where the TextWatcher listener is introduced by means of an anonymous class:
EditText geolocation;
...
...
...
private void geolocation(View v)
{
// Respond to user input
geolocation = (EditText) v.findViewById(R.id.geolocation);
geolocation.addTextChangedListener(new TextWatcher()
{
public void onTextChanged(CharSequence c, int start, int before, int count)
{
residence.setGeolocation(c.toString());
}
public void beforeTextChanged(CharSequence c, int start, int count, int after)
{
// this space intentionally left blank
}
public void afterTextChanged(Editable c)
{
// this space intentionally left blank
}
});
}
Figure 1 provides an outline analysis of the method.
In the following steps we shall develop short programs that will, hopefully, explain more fully how the pattern in this method, geolocation, works.
We shall select names for classes, interface and methods that, where appropriate, are the same as in geolocation.
This approach is for the purpose of instruction only and is unlikely to be used very much in practice.
Here is the TextWatcher code:
Filename: TextWatcher.java
package org.wit.callback;
public interface TextWatcher
{
void onTextChanged(String changedtext);
}
As you can see, TextWatcher is a simple interface with one method.
Next, create a class that implements TextWatcher:
Filename: Callback.java
package org.wit.callback;
public class Callback implements TextWatcher
{
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
}
This class does nothing other than implement the TextWatcher method onTextChanged
The next class we develop is TextView:
Filename: TextView.java
package org.wit.callback;
public class TextView
{
private TextWatcher textwatcher;
private boolean somethingHappened;
public void addTextChangedListener(TextWatcher textwatcher)
{
// Save the event object for later use.
this.textwatcher = textwatcher;
// Nothing to report yet.
somethingHappened = false;
}
// Invoking with flag == true sets scene for a callback
public void setPredicate(boolean flag)
{
somethingHappened = flag;
}
// This method will be invoked repeatedly in an event loop
public void doWork()
{
// Check the predicate, which is set elsewhere.
if (somethingHappened)
{
// Signal the event by invoking the interface's method.
textwatcher.onTextChanged("Finally - you called back");
somethingHappened = false;
}
}
}
Here is a brief analysis of TextView:
Filename: EventLoop.java
package org.wit.callback;
// Class to simulate a short-lived event loop
public class EventLoop
{
public static void main(String[] args)
{
TextWatcher textwatcher = new Callback();
TextView textview = new TextView();
textview.addTextChangedListener(textwatcher);
int val = 0;
// The simulated event loop
do
{
if (val % 100 == 0)
{
textview.setPredicate(true); // the trigger to fire an event
}
// invoke repeatedly but trigger event only when predicate true
textview.doWork();
val += 1;
} while (val < 500);// we expect 5 events to be triggered
}
}
This class simulates an event loop.
In more detail:
Run the application and observe the output:
We shall now dispense with the Callback class
import org.wit.callback.TextView;
import org.wit.callback.TextWatcher;
Study the original EventLoop code:
Note the content of Callback:
Open org.wit.callbackanon.EventLoop.java and:
textview.addTextChangedListener(new TextWatcher()
{
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
});
It should be clear what's happening: we have dispensed with the class Callback and instead used its content within what is referred to as an anonymous class as a parameter to addTextChangedListener..
Here is the final refactored EventLoop code:
Filename: EventLoop.java
package org.wit.callbackanon;
import org.wit.callback.TextView;
import org.wit.callback.TextWatcher;
//Class to simulate a short-lived event loop
public class EventLoop
{
public static void main(String[] args)
{
TextView textview = new TextView();
// We use an anonymous class instead of the Callback object
textview.addTextChangedListener(new TextWatcher()
{
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
});
// The simulated event loop
int val = 0;
do
{
if (val % 100 == 0)
{
textview.setPredicate(true); // the trigger to fire an event
}
textview.doWork();// invoked repeatedly and triggers event when predicate
// true
val += 1;
} while (val < 500);// we expect 5 events to be triggered
}
}
Figure 1 below presents a flow diagram of the program.
Copy the file EventLoop.java from org.wit.callbackanon to the new package.
Reorganize EventLoop
Here is the refactored class:
org.wit.callbackimpl.EventLoop.java
package org.wit.callbackimpl;
import org.wit.callback.TextView;
import org.wit.callback.TextWatcher;
//Class to simulate a short-lived event loop
public class EventLoop
{
private void runloop()
{
TextView textview = new TextView();
// We use an anonymous class instead of the Callback object
textview.addTextChangedListener(new TextWatcher()
{
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
});
// The simulated event loop
int val = 0;
do
{
if (val % 100 == 0)
{
textview.setPredicate(true); // the trigger to fire an event
}
textview.doWork();// invoked repeatedly and triggers event when predicate
// true
val += 1;
} while (val < 500);// we expect 5 events to be triggered
}
public static void main(String[] args)
{
EventLoop obj = new EventLoop();
obj.runloop();
}
}
Test that the result of this change has no noticeable effect on the output:
As shown in Figure 1, replace the anonymous class with the this reference.
This change will trigger an error:
The reason for the error is that the signature of addTextChangedListener clearly shows that a TextWatcher type is expected as a parameter whereas we have supplied it with an EventLoop reference.
We can eliminate the error by:
Change the signature of EventLoop to the following:
public class EventLoop implements TextWatcher
Use QuickFix to override onTextChanged in the class:
@Override
public void onTextChanged(String changedtext)
{
// TODO Auto-generated method stub
}
Finally, fully implement onTextChanged:
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
The application should now be error-free.
Here is the final version of EventLoop:
package org.wit.callbackimpl;
import org.wit.callback.TextView;
import org.wit.callback.TextWatcher;
//Class to simulate a short-lived event loop
public class EventLoop implements TextWatcher
{
public void runloop()
{
TextView textview = new TextView();
// EventLoop implements TextWatcher
// Consequently "this" a legal parameter here
textview.addTextChangedListener(this);
// The simulated event loop
int val = 0;
do
{
if (val % 100 == 0)
{
textview.setPredicate(true); // the trigger to fire an event
}
textview.doWork();// invoked repeatedly, triggers event when predicate true
val += 1;
} while (val < 500);// we expect 5 events to be triggered
}
public static void main(String[] args)
{
EventLoop obj = new EventLoop();
obj.runloop();
}
@Override
public void onTextChanged(String changedtext)
{
System.out.println(changedtext);
}
}
Modify the the Delegate callback method as follows:
Here is the package and file arrangement you are recommended to use:
Here is skeleton code for EventLoop class with hints
package org.wit.callbackexercise;
import java.util.Scanner;
import org.wit.callback.TextView;
//Class to simulate a short-lived event loop
public class EventLoop
{
String keyboardInput;
static Scanner in = new Scanner(System.in);
public void runloop()
{
TextView textview = new Keypress();
// EventLoop implements KeyBoardListener
// Consequently "this" a legal parameter here
textview.addTextChangedListener(...);
// The simulated event loop
do
{
keyboardInput = keyboard();
if (keyboardInput.equals("c"))
{
textview.setPredicate(true); // the trigger to fire an event
}
textview.doWork();//if predicate true then trigger event in doWork
} while (keyboardInput.equals("q") == false);
System.out.println("Thanks for your time - bye");
}
/*
* Capture and return a single keyboard character
*/
public String keyboard()
{
String s = "";
if(in.hasNext())
{
s = in.next();
}
return s;
}
public static void main(String[] args)
{
EventLoop obj = new EventLoop();
obj.runloop();
in.close();
}
}
Here is a skeleton of the derived Keypress class:
package org.wit.callbackexercise;
import org.wit.callback.TextView;
public class Keypress extends TextView
{
public void addKeyBoardListener(KeyBoardListener listener)
{
// Save the event object for later use.
//TODO ...
}
// This method will be invoked repeatedly in an event loop
@Override
public void doWork()
{
// Check the predicate, which is set elsewhere.
if (somethingHappened)
{
// Signal the event by invoking the interface's method.
//TODO: Invoke: onKeyBoardInput();
//TODO: Invoke: onTextChanged("Finally - you called back");
somethingHappened = false;
}
}
}
Here are suggested class diagrams for the solution.
This is downloadable archive of the event handling code complete to the end if this lab: