This demonstration is about implementation of double tap event handling on ListView items. We can do it using only default onItemClickListener and creating our own interface so we can signal all those who implements create

d interface. First create a class that will extend ListView, this means that we will not use regular ListView class but our own custome one and declare some fields and constants that we’ll using later.

 public class CustomListView extends ListView{ private final static int DOUBLE_TAP = 2; //constant for double tap event private final static int SINGLE_TAP = 1; //constant for single tap event private final static int DELAY = ViewConfiguration.getDoubleTapTimeout(); //delay between single and double tap events private boolean mTookFirstEvent=false; private int mPositionHolder=-1; private int mPosition=-1;  private OnItemDoubleTapLister mOnDoubleTapListener = null; private AdapterView mParent = null; private View mView = null; private long mId= 12315; private Message mMessage = null; private static String TAG = ?DoubleTapListView?; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch(msg.what) { case SINGLE_TAP: Log.i(TAG, ?Single tap entry?); mOnDoubleTapListener.OnSingleTap(mParent, mView, mPosition, mId); break; case DOUBLE_TAP: Log.i(TAG, ?Double tap entry?); mOnDoubleTapListener.OnDoubleTap(mParent, mView, mPosition, mId); break; } } }; public CustomListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); removeSelector(); } public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); removeSelector(); } public CustomListView(Context context) { super(context); removeSelector();//optional } 

Then we need to create our custom interface through which we will signal when any of the events occurred:

 public interface OnItemDoubleTapLister { public void OnDoubleTap(AdapterView parent, View view, int position, long id); public void OnSingleTap(AdapterView parent, View view, int position, long id); } 

OnDoubleTap() method will be called whenever double tap event occurred and contrary OnSingleTap() Our class needs to have a method through which we will apply our interface callbacks:

 public void setOnItemDoubleClickListener(OnItemDoubleTapLister listener ) { mOnDoubleTapListener = listener; //If the listener is null then throw exception if(mOnDoubleTapListener==null) throw new IllegalArgumentException(?OnItemDoubleTapListener cannot be null?); else {  //If the OnItemDoubleTapListener is not null, //register the default onItemClickListener to proceed with listening setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView parent, View view, int  position, long id) { mParent = parent; mView = view; mPosition = position; mId=id; if(!mTookFirstEvent) //Testing if first tap occurred { mPositionHolder=position; //this will hold the position variable from first event. //In case user presses any other item (position) mTookFirstEvent=true; mMessage = mMessage == null? new Message() : mHandler.obtainMessage(); //?Recycling? the message, instead creating new instance we get the old one mHandler.removeMessages(SINGLE_TAP); mMessage.what = SINGLE_TAP; mHandler.sendMessageDelayed(mMessage, DELAY); } else { if(mPositionHolder == position) { mHandler.removeMessages(SINGLE_TAP); //Removing the message that was queuing for scheduled //sending after elapsed time > DELAY, //immediately when we have second event, //when the time is < DELAY mPosition = position; mMessage = mHandler.obtainMessage(); //obtaining old message instead creating new one mMessage.what=DOUBLE_TAP; mHandler.sendMessageAtFrontOfQueue(mMessage); //Sending the message immediately when we have second event, //when the time is < DELAY mTookFirstEvent=false; } else { /* When the position is different from previously * stored position in mPositionHolder (mPositionHolder!=position). * Wait for double tap on the new item at position which is * different from mPositionHolder. Setting the flag mTookFirstEvent * back to false. * * However we can ignore the case when we have mPosition!=position, when we want, * to do something when onSingleTap/onItemClickListener are called. * */ mMessage = mHandler.obtainMessage(); mHandler.removeMessages(SINGLE_TAP); mTookFirstEvent=true; mMessage.what = SINGLE_TAP; mPositionHolder = position; mHandler.sendMessageDelayed(mMessage, DELAY); } } } }); } } 

The idea behind is to find a way to differentiate what’s single and what’s double tap. The default timeout that Android uses to distinguish between double and single tap is 300 milliseconds(See: ViewConfiguration source and GestureListener source). So between each taps we give 300ms to the user to do another tap. If the times up, i.e the mHandler sends the delayed message, means that single tap occurred, contrary if another tap occurred, the mHander immediately recycles the message that was queueing for execution, with new value for what field of our mMessage and it posts the message at front of the queue, therefore it is executed immediately when the time of events is less that the DELAY value (see comments). Check regularly this blog for updates on this implementation and on my github and google code

AUTHOR: Nikola Despotoski
1 Comment
  • That’s 2 ceelvr by half and 2×2 clever 4 me. Thanks!

    January 22, 2012