Objectives

This is a short lab whose aim is to provide a detailed explanation of event handling via a simple standalone non-android application

MyRentFragment

Here is an extract from MyRentFragment.java:

  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.

Figure 1: Analysis of callback and listener code

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.

Callback (Standalone method)

This approach is for the purpose of instruction only and is unlikely to be used 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:

Figure 1: Callback in action

Callback (Anonymous class method)

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.

Figure 1: Flow diagram of callback program

Callback (Delegated method)

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();
  }    
}

Figure 1

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);

  }
}

Exercise 1

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.

Archives

This is downloadable archive of the event handling code complete to the end if this lab: