Handling events appropriately is an important aspect of developing any Android Application.
A listener that is an interface with callback functions is used by Android to do the dispatch of event.
When user triggers an event of one component, OS will check whether there is a user level listener registered for it. If yes, callback function will be called so user program can do action for the event.
According to the number of listeners can be registered for the event, there are two kinds of event, single-listener event and multiple-listener event. For single-listener event, only one listener is allowed, and for multiple-listener event, there can be more than one listener.
Take View for example, as shown in below table, there are 12 listeners:
Event | Listener | single-listener | multiple-listener |
---|---|---|---|
Attach state change | OnAttachStateChangeListener | X | |
Click | OnClickListener | X | |
Create of context menu | OnCreateContextMenuListener | X | |
Drag | OnDragListener | X | |
Focus change | OnFocusChangeListener | X | |
Generic motion | OnGenericMotionListener | X | |
Hover | OnHoverListener | X | |
Hardware key | OnKeyListener | X | |
Layout change | OnLayoutChangeListener | X | |
Long click | OnLongClickListener | X | |
Status bar visibility change | OnSystemUiVisibilityChangeListener | X | |
Touch | OnTouchListener | X |
Two of them (OnAttachStateChangeListener and OnLayoutChangeListener) are used for multiple-listener event, and the rest ten of them are used for single-listener event.
We must register corresponding listener to the target object if we want to act for an event from it. By convention, member function setListenerName is used for the registration of single listener and addListenerName is used for the registration of multiple listeners. For single-listener registration function, if called multiple times, only the last one is effective.
As we explained earlier, there are three types of android app menus for which you can write events and handle it appropriately.
Register a Listener
The listener registration function accepts two kinds of inputs: an interface object, and an object of class that implements the interface. For the later, a named, anonymous class/interface and even the parent activity is applicable. As one view only has one activity, so the last method is not practical for multiple-listener event.
1. Interface Variable
TextView tv = (TextView) this.findViewById(R.id.hello_world); //Interface View.OnClickListener m_click_itf = new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } }; tv.setOnClickListener(m_click_itf);
2. Named Class
TextView tv = (TextView) this.findViewById(R.id.hello_world); //Named Class class ClickHandler implements View.OnClickListener { @Override public void onClick(View v) { // TODO Auto-generated method stub } } ClickHandler m_click_lister = new ClickHandler(); tv.setOnClickListener(m_click_lister);
3. Anonymous Class/Interface
TextView tv = (TextView) this.findViewById(R.id.hello_world); //Anonymous Class tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } });
4. Activity
public class MainActivity extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) this.findViewById(R.id.hello_world); tv.setOnClickListener(this); } @Override public void onClick(View v) { // TODO Auto-generated method stub } }
Action for an event
Technically it is an efficient way to assign a common stub listener for a group of objects. There is a View object passed to the callback which is the owner of the event. For the object created by layout XML resource, as ID is unique across the project, it is safe to use ID as a distinguisher.
@Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.hello_world: //act for the click } }
Sometimes, it may be a good idea to assign the IDs from 0 to n for programmatically created components, and use the ID as an index for more efficient data processing. But if there are two groups declare an overlapped id range, it would be better use separate listener for different groups, so that ID will be unique inside group.
Why multi-listener?
There are at least two agents in android system, the SDK and the user. If both of them want to know the status of an event, it must be defined as a multi-listener event. Take OnLayoutChangeListener for example. LayoutTransition.java in animate package need the layout changed event to do the animate properly, while user maybe also intent to do some operations at the same time. So, the OnLayoutChangeListener can be registered for multiple times.
Mechanism of the listener
We also take View.onClickListener for example. When user program call the setOnClickListener, the interface is stored in a ListenerInfo object.
The View class filters the click event from pointer event dispatch: dispatchPointerEvent -> dispatchTouchEvent -> onTouchEvent -> performClick. In the performClick, user registered callback will be called.
public void setOnClickListener(OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; } public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); return true; } return false; }
Comments on this entry are closed.
Thanks. Very timely and useful article