12/08/2018, 16:42

Creating custom Listeners

In Android development, the "listener" or "observer" pattern is the most common strategy when creating asynchronous event that runs a specific code when an events occurs. This pattern is also used with any type of I/O as well as for view events on screen. Below is a common usage of the listener ...

In Android development, the "listener" or "observer" pattern is the most common strategy when creating asynchronous event that runs a specific code when an events occurs. This pattern is also used with any type of I/O as well as for view events on screen. Below is a common usage of the listener pattern to attach a click event to a button:

Button btnOne = (Button) findViewById(R.id.btnExample);
btnOne.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(getActivity(), "CLICKED!", 
   Toast.LENGTH_LONG).show();
    }
});

Remember, there are various listeners provided by android by default. The listeners can be classified into 3 parts namely:

  1. Event Listeners: An event listener is an interface in the View class that contains a single callback method. These methods will be called by the Android framework when the View to which the listener has been registered is triggered by user interaction with the item in the UI.
  2. Event Listeners Registration: Event Registration is the process by which an Event Handler gets registered with an Event Listener so that the handler is called when the Event Listener fires the event.
  3. Event Handlers: When an event happens and we have registered an event listener for the event, the event listener calls the Event Handlers, which is the method that actually handles the event. Event Listeners & Event Handlers consist of various built-in listener which are used comonly. Examples are OnClick(), onLongClick(), onMenuItemClick(). OnTouch() and so on.

Why the need for custom Listeners

We can create our own listeners and attach callbacks to the events they fire from other areas in our code. Because a listener is useful anytime a child object wants to emit events upwards to notify a parent object and allow that object to respond accordingly, custom listeners is useful in a variety of cases including:

  • Firing events from list items upwards to an activity from within an adapter
  • Firing async events from an abstraction (i.e networking library) to a parent handler.
  • Firing events from a fragment upwards to an activity.

Using listners one can properly separate concerns in the code. So without further adue lets get into creating custom listeners.

Custom Listeners

There are basically four steps in creating custom listeners and they are as follow:

  1. Define the Interface: Start by defining an interface in the child object or in a stand alone interface file. This object can be a plain java object, an Adapter, a Fragment, or any object created by a "parent" object such as an activity which will handle the events that are triggered.
public class MyCustomObject {
  // Below is our interface declaration
  public interface MyCustomObjectListener {
      // These methods are the different events and need to pass relevant arguments related to the event triggered
      public void onObjectReady(String title);
      // or when data has been loaded
      public void onDataLoaded(SomeData data);
  }
}
  1. Create Listener Setter: Now that we have the interface defined, we can setup a listener variable to store a particular implementation of the callbacks which will be defined by the owner.
public class MyCustomObject {
    // ...

    // Step 2 - This variable represents the listener passed in by the owning object
    // The listener must implement the events interface and passes messages up to the parent.
    private MyCustomObjectListener listener;
}

as well as a "setter" which allows the listener callbacks to be defined in the parent object:

public class MyCustomObject {
    // Constructor where listener events are ignored
    public MyCustomObject() {
        // set null or default listener or accept as argument to constructor
        this.listener = null; 
    }
	
    // Assign the listener implementing events interface that will receive the events
    public void setCustomObjectListener(MyCustomObjectListener listener) {
        this.listener = listener;
    }
}

Using this setter will allow the parent object to pass in an implementation of the listener after this child object is created similar to how the button accepts the click listener. 3. Implement Listener Callback: Now that we have created the setters, the parent object can construct and set callbacks for the child object by:

public class MyParentActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	// ...
    	// Create the custom object
    	MyCustomObject object = new MyCustomObject();
    	// Step 4 - Setup the listener for this object
    	object.setCustomObjectListener(new MyCustomObject.MyCustomObjectListener() {
    		@Override
    		public void onObjectReady(String title) {
    		    // Code to handle object ready
    		}
    		
    		@Override
    		public void onDataLoaded(SomeData data) {
    		    // Code to handle data loaded from network
    		    // Use the data here!
    		}
    	});
    }
}

We have created the child object and passed in the callback implementation for the listener. These custom events can now be fired by the child object to pass the event to the parent as appropriate. 4.Trigger Listener Events Now the child object should trigger events to the listener whenever appropriate and pass along the data to the parent object through the event. These events can be triggered when a user action is taken or when an asynchronous task such as networking or persistence is completed. For example, we will trigger the onDataLoaded(SomeData data) event once this network request comes back on the child:

public class MyCustomObject {
   // Listener defined earlier
   public interface MyCustomObjectListener {
      public void onObjectReady(String title);
      public void onDataLoaded(SomeData data);
   }
   
   // Member variable was defined earlier
   private MyCustomObjectListener listener;

    // Constructor where listener events are ignored
    public MyCustomObject() {
        // set null or default listener or accept as argument to constructor
        this.listener = null; 
        loadDataAsync();
    }
  
    // ... setter defined here as shown earlier

    public void loadDataAsync() {
        AsyncHttpClient client = new AsyncHttpClient();
        client.get("https://mycustomapi.com/data/get.json", new JsonHttpResponseHandler() {         
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                // Networking is finished loading data, data is processed
                SomeData data = SomeData.processData(response.get("data"));
                // Do some other stuff as needed....
                // Now let's trigger the event
                if (listener != null)
                  listener.onDataLoaded(data); // <---- fire listener here
            }
        });
    }
}

As you may habe noticed, the listener is fired when the async task is completed and our data is ready.

Conclusion

Custom listeners are used in "listener pattern" and is a very powerful Java pattern that can be used to emit events to a single parent in order to communicate important information asynchronously. This can be used to move complex logic out of adapters, create useful abstractions for your code or communicate from a fragment to your activities.

0