Android: How to detect double touch?

I have a double tap problem. Well, I implemented onGestureListener , and I had a gestureDetector , but I'm not sure where the problem is, here is my code:

  public class home extends TabActivity implements OnGestureListener { /** Called when the activity is first created. */ private EditText queryText; private ResultsAdapter m_adapter; private ProgressDialog pd; final Handler h = new Handler(); private TabHost mTabHost; private ArrayList<SearchItem> sResultsArr = new ArrayList<SearchItem>(); private String queryStr; private JSONObject searchResponse; private GestureDetector gestureScanner; final Runnable mUpdateResults = new Runnable() { public void run() { updateListUi(); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button search = (Button)findViewById(R.id.search); Button testButt = (Button)findViewById(R.id.testbutt); queryText = (EditText)findViewById(R.id.query); ListView lvr = (ListView)findViewById(R.id.search_results); //initialise the arrayAdapter this.m_adapter = new ResultsAdapter(home.this, R.layout.listrow, sResultsArr); lvr.setAdapter(this.m_adapter); lvr.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { // TODO Auto-generated method stub pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false); } }); gestureScanner = new GestureDetector(this,this); gestureScanner.setOnDoubleTapListener(new OnDoubleTapListener(){ public boolean onDoubleTap(MotionEvent e) { //viewA.setText("-" + "onDoubleTap" + "-"); pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false); return false; } public boolean onDoubleTapEvent(MotionEvent e) { // viewA.setText("-" + "onDoubleTapEvent" + "-"); return false; } public boolean onSingleTapConfirmed(MotionEvent e) { //viewA.setText("-" + "onSingleTapConfirmed" + "-"); return false; } }); //initialise tab contents mTabHost = getTabHost(); mTabHost.addTab(mTabHost.newTabSpec("tab1").setIndicator("Home").setContent(R.id.homepage)); mTabHost.addTab(mTabHost.newTabSpec("tab2").setIndicator("Search Results").setContent(R.id.tab2)); mTabHost.setCurrentTab(0); //sets the respective listeners testButt.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { if(mTabHost.getTabWidget().getVisibility()==View.GONE){ mTabHost.getTabWidget().setVisibility(View.VISIBLE); } else{ mTabHost.getTabWidget().setVisibility(View.GONE); } } }); search.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { sResultsArr.clear(); queryStr = "http://rose.mosuma.com/mobile?query=" + queryText.getText().toString(); pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false); goSearch(); } }); } //updates the listUI whenever after receiving the response from the server public void updateListUi(){ if(sResultsArr.size() > 0){ } try{ String ptypename; int count; LinearLayout ptypebar = (LinearLayout)findViewById(R.id.productCat); ptypebar.removeAllViews(); JSONArray ptypes = searchResponse.getJSONArray("ptypes"); for(int index =0;index < ptypes.length();index++){ JSONObject ptype = ptypes.getJSONObject(index); count = ptype.getInt("count"); ptypename = ptype.getString("ptypename"); //add into tab 2 UI //ImageView icon = new ImageView(this); TextView t = new TextView(home.this); t.setText(ptypename + " (" + count + ")"); ptypebar.addView(t); } } catch(JSONException e){ } //if(m_adapter.getItems() != sResultsArr){ ArrayList<SearchItem> a = m_adapter.getItems(); a = sResultsArr; //} m_adapter.notifyDataSetChanged(); pd.dismiss(); } public void goSearch(){ mTabHost.setCurrentTab(1); //separate thread for making http request and updating the arraylist Thread t = new Thread() { public void run() { searchResponse = sendSearchQuery(queryStr); try{ JSONArray results = searchResponse.getJSONArray("results"); //this is stupid. i probably have to see how to make a json adapter for(int index =0;index < results.length();index++){ JSONObject product = results.getJSONObject(index); //gets the searched products from the json object URL imgUrl = new URL(product.getString("image")); String productname = product.getString("productname"); String ptypename = product.getString("ptypename"); int pid = product.getInt("pid"); int positive = product.getInt("pos"); int negative = product.getInt("neg"); int neutral = product.getInt("neu"); SearchItem item = new SearchItem(imgUrl,productname,ptypename,neutral,positive,negative,pid); sResultsArr.add(item); } } catch(JSONException e){ } catch(Exception e){ } //returns back to UI therad h.post(mUpdateResults); } }; t.start(); } //sends a request with qry as URL //and receives back a JSONobject as response public JSONObject sendSearchQuery(String qry){ HttpRequest r = new HttpRequest(); JSONObject response = r.sendHttpRequest(qry); return response; } @Override public boolean onDown(MotionEvent arg0) { return gestureScanner.onTouchEvent(arg0); } @Override public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { // TODO Auto-generated method stub return false; } @Override public void onLongPress(MotionEvent arg0) { // TODO Auto-generated method stub } @Override public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent arg0) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent arg0) { // TODO Auto-generated method stub return false; } 

Oh, another question, if my ListView has onItemClickListener , can android detection between single click or double click for it?

+52
android listview double-click
Feb 07 2018-10-02T00
source share
17 answers

Why don't you use a long press? Or are you using this already for something else? Long Press advantages over double touch:

  • A long press is the recommended interaction in the User Guide, Double Touch is not.
  • This is what users expect; the user may not find the Double Touch action because he will not search for it.
  • It is already being processed in the API .
  • The implementation of Double Touch will affect the processing of Single Touches, because you will have to wait to see if each Single Touch turns into a double touch before you can process it.
+15
Feb 07 '10 at 21:19
source share

You can use the GestureDetector. See the following code:

 public class MyView extends View { GestureDetector gestureDetector; public MyView(Context context, AttributeSet attrs) { super(context, attrs); // creating new gesture detector gestureDetector = new GestureDetector(context, new GestureListener()); } // skipping measure calculation and drawing // delegate the event to the gesture detector @Override public boolean onTouchEvent(MotionEvent e) { return gestureDetector.onTouchEvent(e); } private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } // event when double tap occurs @Override public boolean onDoubleTap(MotionEvent e) { float x = e.getX(); float y = e.getY(); Log.d("Double Tap", "Tapped at: (" + x + "," + y + ")"); return true; } } } 

You can override other listener methods to get single clicks, clicks, and so on.

+99
Jun 09 '11 at 8:28
source share

As an easy alternative to GestureDetector you can use this class

 public abstract class DoubleClickListener implements OnClickListener { private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds long lastClickTime = 0; @Override public void onClick(View v) { long clickTime = System.currentTimeMillis(); if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){ onDoubleClick(v); } else { onSingleClick(v); } lastClickTime = clickTime; } public abstract void onSingleClick(View v); public abstract void onDoubleClick(View v); } 

Example:

  view.setOnClickListener(new DoubleClickListener() { @Override public void onSingleClick(View v) { } @Override public void onDoubleClick(View v) { } }); 
+58
Feb 13 '14 at 20:08
source share

combining Bughi, DoubleClickListner, and Jayant Arora Timer into one containing class:

 public abstract class DoubleClickListener implements OnClickListener { private Timer timer = null; //at class level; private int DELAY = 400; private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds long lastClickTime = 0; @Override public void onClick(View v) { long clickTime = System.currentTimeMillis(); if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){ processDoubleClickEvent(v); } else { processSingleClickEvent(v); } lastClickTime = clickTime; } public void processSingleClickEvent(final View v){ final Handler handler=new Handler(); final Runnable mRunnable=new Runnable(){ public void run(){ onSingleClick(v); //Do what ever u want on single click } }; TimerTask timertask=new TimerTask(){ @Override public void run(){ handler.post(mRunnable); } }; timer=new Timer(); timer.schedule(timertask,DELAY); } public void processDoubleClickEvent(View v){ if(timer!=null) { timer.cancel(); //Cancels Running Tasks or Waiting Tasks. timer.purge(); //Frees Memory by erasing cancelled Tasks. } onDoubleClick(v);//Do what ever u want on Double Click } public abstract void onSingleClick(View v); public abstract void onDoubleClick(View v); } 

and may be referred to as:

 view.setOnClickListener(new DoubleClickListener() { @Override public void onSingleClick(View v) { } @Override public void onDoubleClick(View v) { } }); 
+14
Nov 15 '16 at 18:22
source share

if you do not want to search for a custom view, you can use the following approach. e.g. ImageView

 // class level GestureDetector gestureDetector; boolean tapped; ImageView imageView; // inside onCreate of Activity or Fragment gestureDetector = new GestureDetector(context,new GestureListener()); 

// --------------------------------------------- --- --------------------------------

 public class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } // event when double tap occurs @Override public boolean onDoubleTap(MotionEvent e) { tapped = !tapped; if (tapped) { } else { } return true; } } 

// --------------------------------------------- --- --------------------------------

for imageview

 imageView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub return gestureDetector.onTouchEvent(event); } }); 
+7
Apr 26 '13 at 9:13
source share

This is my solution, it uses the default value of setOnItemClickListener() . I had the same task to implement. Soon I will post an example and a custom class to my github. A brief explanation is given. I'm not sure if the time in milliseconds is the correct difference for the system (see ViewConfiguration.getDoubleTapTimeout() source) to solve single and double click.

Edit: See here: https://github.com/NikolaDespotoski/DoubleTapListView or https://github.com/NikolaDespotoski/DoubleTapListViewHandler

+4
Jan 06 2018-12-12T00:
source share

GuestureDetecter works well on most devices, I would like to know how to adjust the time between two clicks of the mouse with a double click, I could not do this. I updated the above “Bughi” code “DoubleClickListner”, added a timer using a handler that executes the code after a certain delay with one click, and if a double click is executed before this delay, it cancels the timer and a single task and only double-clicks the task. The code works. Perfectly makes it ideal for use as a double click:

  private Timer timer = null; //at class level; private int DELAY = 500; view.setOnClickListener(new DoubleClickListener() { @Override public void onSingleClick(View v) { final Handler handler = new Handler(); final Runnable mRunnable = new Runnable() { public void run() { processSingleClickEvent(v); //Do what ever u want on single click } }; TimerTask timertask = new TimerTask() { @Override public void run() { handler.post(mRunnable); } }; timer = new Timer(); timer.schedule(timertask, DELAY); } @Override public void onDoubleClick(View v) { if(timer!=null) { timer.cancel(); //Cancels Running Tasks or Waiting Tasks. timer.purge(); //Frees Memory by erasing cancelled Tasks. } processDoubleClickEvent(v);//Do what ever u want on Double Click } }); 
+4
Oct 10 '14 at 10:14
source share
 boolean nonDoubleClick = true, singleClick = false; private long firstClickTime = 0L; private final int DOUBLE_CLICK_TIMEOUT = 200; listview.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int pos, long id) { // TODO Auto-generated method stub Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub if (singleClick) { Toast.makeText(getApplicationContext(), "Single Tap Detected", Toast.LENGTH_SHORT).show(); } firstClickTime = 0L; nonDoubleClick = true; singleClick = false; } }, 200); if (firstClickTime == 0) { firstClickTime = SystemClock.elapsedRealtime(); nonDoubleClick = true; singleClick = true; } else { long deltaTime = SystemClock.elapsedRealtime() - firstClickTime; firstClickTime = 0; if (deltaTime < DOUBLE_CLICK_TIMEOUT) { nonDoubleClick = false; singleClick = false; Toast.makeText(getApplicationContext(), "Double Tap Detected", Toast.LENGTH_SHORT).show(); } } } }); 
+3
Oct 27 '15 at 15:21
source share

Improvised Dhruvi Code

 public abstract class DoubleClickListener implements View.OnClickListener { private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds long lastClickTime = 0; boolean tap = true; @Override public void onClick(View v) { long clickTime = System.currentTimeMillis(); if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){ onDoubleClick(v); tap = false; } else tap = true; v.postDelayed(new Runnable() { @Override public void run() { if(tap) onSingleClick(); } },DOUBLE_CLICK_TIME_DELTA); lastClickTime = clickTime; } public abstract void onDoubleClick(View v); public abstract void onSingleClick(); } 
+2
Jan 03 '18 at 21:05
source share

My solution may be helpful.

 long lastTouchUpTime = 0; boolean isDoubleClick = false; private void performDoubleClick() { long currentTime = System.currentTimeMillis(); if(!isDoubleClick && currentTime - lastTouchUpTime < DOUBLE_CLICK_TIME_INTERVAL) { isDoubleClick = true; lastTouchUpTime = currentTime; Toast.makeText(context, "double click", Toast.LENGTH_SHORT).show(); } else { lastTouchUpTime = currentTime; isDoubleClick = false; } } 
+1
Sep 02 '15 at 8:19
source share

Single and double click implementation

 public abstract class DoubleClickListener implements View.OnClickListener { private static final long DOUBLE_CLICK_TIME_DELTA = 200; private long lastClickTime = 0; private View view; private Handler handler = new Handler(); private Runnable runnable = new Runnable() { @Override public void run() { onSingleClick(view); } }; private void runTimer(){ handler.removeCallbacks(runnable); handler.postDelayed(runnable,DOUBLE_CLICK_TIME_DELTA); } @Override public void onClick(View view) { this.view = view; long clickTime = System.currentTimeMillis(); if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){ handler.removeCallbacks(runnable); lastClickTime = 0; onDoubleClick(view); } else { runTimer(); lastClickTime = clickTime; } } public abstract void onSingleClick(View v); public abstract void onDoubleClick(View v); 

}

+1
Feb 24 '16 at 13:45
source share
 public class MyView extends View { GestureDetector gestureDetector; public MyView(Context context, AttributeSet attrs) { super(context, attrs); // creating new gesture detector gestureDetector = new GestureDetector(context, new GestureListener()); } // skipping measure calculation and drawing // delegate the event to the gesture detector @Override public boolean onTouchEvent(MotionEvent e) { return gestureDetector.onTouchEvent(e); } private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } // event when double tap occurs @Override public boolean onDoubleTap(MotionEvent e) { float x = e.getX(); float y = e.getY(); Log.d("Double Tap", "Tapped at: (" + x + "," + y + ")"); return true; } } } 
+1
Jun 09 '16 at 12:38 on
source share

Solution from bughi and Jayant Arora for copypast:

 public abstract class DoubleClickListener implements View.OnClickListener { private int position; private Timer timer; private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds long lastClickTime = 0; public DoubleClickListener (int position) { this.position = position; } @Override public void onClick(View v) { long clickTime = System.currentTimeMillis(); if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){ if (timer != null) { timer.cancel(); //Cancels Running Tasks or Waiting Tasks. timer.purge(); //Frees Memory by erasing cancelled Tasks. } onDoubleClick(v, position); } else { final Handler handler = new Handler(); final Runnable mRunnable = () -> { onSingleClick(v, position); }; TimerTask timertask = new TimerTask() { @Override public void run() { handler.post(mRunnable); } }; timer = new Timer(); timer.schedule(timertask, DOUBLE_CLICK_TIME_DELTA); } lastClickTime = clickTime; } public abstract void onSingleClick(View v, int position); public abstract void onDoubleClick(View v, int position);} 
0
Jun 16 '16 at 11:26
source share

Equivalent C # code that I used to implement the same functionality and can even configure to accept N number of clicks

 public interface IOnTouchInterface { void ViewTapped(); } public class MultipleTouchGestureListener : Java.Lang.Object, View.IOnTouchListener { int clickCount = 0; long startTime; static long MAX_DURATION = 500; public int NumberOfTaps { get; set; } = 7; readonly IOnTouchInterface interfc; public MultipleTouchGestureListener(IOnTouchInterface tch) { this.interfc = tch; } public bool OnTouch(View v, MotionEvent e) { switch (e.Action) { case MotionEventActions.Down: clickCount++; if(clickCount == 1) startTime = Utility.CurrentTimeSince1970; break; case MotionEventActions.Up: var currentTime = Utility.CurrentTimeSince1970; long time = currentTime - startTime; if(time <= MAX_DURATION * NumberOfTaps) { if (clickCount == NumberOfTaps) { this.interfc.ViewTapped(); clickCount = 0; } } else { clickCount = 0; } break; } return true; } } public static class Utility { public static long CurrentTimeSince1970 { get { DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local); DateTime dtNow = DateTime.Now; TimeSpan result = dtNow.Subtract(dt); long seconds = (long)result.TotalMilliseconds; return seconds; } } } 

Currently, the code above takes 7 as the number of clicks before it raises the View Tapped event. But it can be configured under any number

0
Jan 30 '18 at 4:08
source share

Double touch and one touch

Just double tap

Using the SimpleOnGestureListener (as shown in the answer by Hannes Niederhausen ) it is quite simple to detect double-clicking on the view.

 private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onDoubleTap(MotionEvent e) { return true; } } 

I don't see much benefit in reinventing the logic for this (as in Boogie's answer ).

Double and one touch with a delay

You can also use SimpleOnGestureListener to distinguish between one-touch and double-touch as mutually exclusive events. To do this, simply override onSingleTapConfirmed . This will delay one-click code execution until the system ViewConfiguration.getDoubleTapTimeout() that the user double-clicks (i.e. ViewConfiguration.getDoubleTapTimeout() > ViewConfiguration.getDoubleTapTimeout() ). There is definitely no reason to reinvent all logic for this (as done in this , this and other answers).

 private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { return true; } @Override public boolean onDoubleTap(MotionEvent e) { return true; } } 

Double and one touch without delay

A potential problem with onSingleTapConfirmed is delay. Sometimes a noticeable delay is unacceptable. In this case, you can replace onSingleTapConfirmed with onSingleTapUp .

 private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public boolean onDoubleTap(MotionEvent e) { return true; } } 

However, you should understand that onSingleTapUp and onDoubleTap will be called when double clicked. (Essentially, this is what Bughi does and what some commentators have complained about.) You need to either use a delay or call both methods. It is not possible to perform a single press without delay and at the same time know if the user is about to press again.

If one-touch delay is unacceptable to you, you have several options:

  • Accept that both onSingleTapUp and onDoubleTap will be called for double tap. Just split your logic appropriately so that it doesn't matter. Essentially, this is what I did when I implemented a double-tap for caps-lock on a user keyboard.
  • Do not use double tap. This is not an intuitive user interface for most things. As Dave Webb suggests , a long press is probably better. You can also implement this with SimpleOnGestureListener :

     private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { } } 
0
Feb 17 '18 at 19:00
source share

I implemented a simple custom method using kotlin coroutines (for Java it can be done via threads).

 var click = 0 view.setOnClickListener{ click++ clicksHandling() } fun clicksHandling() { if (click == 1) { launch { delay(300) // custom delay duration between clicks // if user didn't double tap then click counter still 1 if (click == 1) { // single click handling runOnUiThread { // whatever you wanna do on UI thread } } click = 0 //reset counter , this will run no matter single / double tap } //double click handling if (click == 2) { // whatever on double click } } 
0
Dec 11 '18 at 9:24
source share

Thread + Interface = DoubleTapListener, AnyTap listener, etc.

In this example, I implemented a DoubleTap listener with a stream. You can add my listener with any View object, as with any ClickListener. Using this approach, you can easily listen to any kind of clicks.

yourButton.setOnClickListener(new DoubleTapListener(this));

1) My Listrener class

 public class DoubleTapListener implements View.OnClickListener{ private boolean isRunning= false; private int resetInTime =500; private int counter=0; private DoubleTapCallback listener; public DoubleTapListener(Context context) { listener = (DoubleTapCallback)context; Log.d("Double Tap","New"); } @Override public void onClick(View v) { if(isRunning) { if(counter==1) listener.onDoubleClick(v); } counter++; if(!isRunning) { isRunning=true; new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(resetInTime); isRunning = false; counter=0; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } } 

2) Callback listener

 public interface DoubleTapCallback { public void onDoubleClick(View v); } 

3) Implement in your activities

 public class MainActivity extends AppCompatActivity implements DoubleTapCallback{ private Button button; private int counter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button); button.setOnClickListener(new DoubleTapListener(this)); // Set mt listener } @Override public void onDoubleClick(View v) { counter++; textView.setText(counter+""); } 

Relevant link:

You can see the full working code HERE

0
Jan 26 '19 at 15:41
source share



All Articles