Android: How to detect double-tap?
Asked Answered
S

29

75

I have a problem with implementing double tap. Well I implemented the onGestureListener and I had the gestureDetector, but I'm not sure where is the problem, 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's 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 an onItemClickListener, can android detect between single tap or double tap for it?

Sunrise answered 7/2, 2010 at 18:2 Comment(1)
The title question is misleading. Who would want to destroy an android?Acidic
L
24

Why aren't you using a Long Press? Or are you using that already for something else? The advantages of a Long Press over a Double Touch:

  • Long Press is a recommeded interaction in the UI Guidelines, Double Touch is not.
  • It's what users expect; a user might not find a Double Touch action as they won't go looking for it
  • It's already handled in the API.
  • Implementing a Double Touch will affect handling of Single Touches, because you'll have to wait to see if every Single Touch turns into a Double Touch before you can process it.
Lorenlorena answered 7/2, 2010 at 21:19 Comment(7)
hmmm im trying to implementing something like dolphin browser's action of hiding and showing the tabs (due to a constraint of space)... i had long press implemented for my listview already.. so i'll need a double tapSunrise
Fair enough, I just want to make sure you weren't forgetting about long press.Lorenlorena
how user will know that he has to longpress to activate the listener ? he may click and find it not working .Urania
Yes, I think this answer is pretty good. I really met the conflict for touch down and double tap and single tap. It bothers me a lot for this issue and finally, I think long press is a good choice.Expect
A good example for a double click listener would be a videoview to make it view in full screen.Acriflavine
BTW, longpress is no longer always favored over double-tap on android; both are recommended gestures, depending on common usage: google.com/design/spec/patterns/gestures.html#Flotage
@Flotage I see double-tap usage only as instant zoom action. Where longpress can select an item, initiate drag. Here is new link to gestures in Material design.Giacinta
H
116

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 methods of the listener to get single taps, flinges and so on.

Hm answered 9/6, 2011 at 8:28 Comment(5)
Okay after re-reading your post, I guess your problem is, that you don't return true in the onDown method. the gesturedetectors onTouch is called in your onTouch method.Hm
@Sunrise can you mark this as the answer? This post does answer the question and is useful to future googlers looking specifically for how to implement double tap. Using long tap over double tap is not always an option.Horology
gestureDetector is depricatedVarsity
@Nepster No, is not deprecated, only two of its constructors are.Grams
For even more details and a few more examples, check out this highly voted answer: https://mcmap.net/q/270347/-passing-motionevents-from-recyclerview-onitemtouchlistener-to-gesturedetectorcompatTandem
L
78

As a lightweight 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) {

        }
    });
Lasonde answered 13/2, 2014 at 20:8 Comment(6)
This does not work for single click. You should register some timer (e.g. using Handler) to execute single click when the second click does not occurs within the delta.Reremouse
it does work for single click. When a double click is performed you get a call to onSingleClick() then one to onDoubleClick() which is fine for my use case. Adding a timer would remove that call to onSingleClick() before onDoubleClick() but having a delay before the event is triggered is not acceptable in my caseLasonde
I see. Logically, they are supposed to be exclusive i.e. either onSingleClick() gets called or onDoubleClick() does so you should have waited to decide which one to invoke to avoid later bugs. This is the way Android handle long click (see onTouchEvent(event) method in View class). Also, your delta is so high, 200ms is more reasonable. Normally on PC, I don't think anyone notice that double-click delay.Reremouse
This one worked for me. My code is set up so that I can only use an onTouchListener and I need to detect double clicks. However, using this as a base and with some slight modifications, I got it to work. Thanks for this great idea!Flounder
this is only solution which is working in my case..I have tried 6-7 ways but nothing worked but this is a +10 solution ..Quintonquintuple
It should be noted that this class does not work and needs modification with a CountDownTimer or similarDemonstrate
L
24

Why aren't you using a Long Press? Or are you using that already for something else? The advantages of a Long Press over a Double Touch:

  • Long Press is a recommeded interaction in the UI Guidelines, Double Touch is not.
  • It's what users expect; a user might not find a Double Touch action as they won't go looking for it
  • It's already handled in the API.
  • Implementing a Double Touch will affect handling of Single Touches, because you'll have to wait to see if every Single Touch turns into a Double Touch before you can process it.
Lorenlorena answered 7/2, 2010 at 21:19 Comment(7)
hmmm im trying to implementing something like dolphin browser's action of hiding and showing the tabs (due to a constraint of space)... i had long press implemented for my listview already.. so i'll need a double tapSunrise
Fair enough, I just want to make sure you weren't forgetting about long press.Lorenlorena
how user will know that he has to longpress to activate the listener ? he may click and find it not working .Urania
Yes, I think this answer is pretty good. I really met the conflict for touch down and double tap and single tap. It bothers me a lot for this issue and finally, I think long press is a good choice.Expect
A good example for a double click listener would be a videoview to make it view in full screen.Acriflavine
BTW, longpress is no longer always favored over double-tap on android; both are recommended gestures, depending on common usage: google.com/design/spec/patterns/gestures.html#Flotage
@Flotage I see double-tap usage only as instant zoom action. Where longpress can select an item, initiate drag. Here is new link to gestures in Material design.Giacinta
S
17

combining "Bughi" "DoubleClickListner" and "Jayant Arora" Timer in one contained 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 can be called as :

view.setOnClickListener(new DoubleClickListener() {

            @Override
            public void onSingleClick(View v) {

            }

            @Override
            public void onDoubleClick(View v) {

            }
        });
Stillness answered 15/11, 2016 at 18:22 Comment(2)
Handler is better than TimerTask. Use handler.postDelayed().Common
Writing all this logic for the single-tap delay is unnecessary if you use onSingleTapConfirmed(). See this answer.Common
L
8

if you do not wish to go for custom view then you can use 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);
        }

    });
Lapsus answered 26/4, 2013 at 9:13 Comment(0)
C
5

Double-tap and Single-tap

Double-tap only

It is quite easy to detect a double tap on a view by using SimpleOnGestureListener (as demonstrated in Hannes Niederhausen's answer).

private class GestureListener extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        return true;
    }
}

I can't see a big advantage to re-inventing the logic for this (like bughi's answer).

Double-tap and Single-tap with delay

You can also use the SimpleOnGestureListener to differentiate a single-tap and a double-tap as mutually exclusive events. To do that you just override onSingleTapConfirmed. This will delay running the single-tap code until the system is certain that the user hasn't double-tapped (ie, the delay > ViewConfiguration.getDoubleTapTimeout()). There is definately no reason to re-invent all the logic for that (as is 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-tap and Single-tap with no delay

The potential problem with onSingleTapConfirmed is the delay. Sometimes a noticeable delay is not acceptable. In that 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;
    }
}

You need to realize, though, that both onSingleTapUp and onDoubleTap will be called if there is a double-tap. (This is essentially what bughi's answer does and what some of the commenters were complaining about.) You either need to use the delay or call both methods. It's not possible have a single-tap with no delay and at the same time know whether the user is going to tap again.

If the single-tap delay is not acceptable for you then you have a couple options:

  • Accept that both onSingleTapUp and onDoubleTap will be called for a double-tap. Just divide up your logic appropriately so that it doesn't matter. This is essentially what I did when I implemented a double-tap for caps-lock on a custom keyboard.
  • Don't use a double-tap. It's not an intuitive UI action for most things. As Dave Webb suggests, a long press is probably better. You can also implement that with the 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) {
    
        }
    }
    
Common answered 17/2, 2018 at 19:0 Comment(0)
C
4

This is my solution, it uses default setOnItemClickListener(). I had the same task to implement. Soon I'll post example and custom class on my github. Brief explanation is given. I'm not sure if the time in milliseconds is right difference for the system (See ViewConfiguration.getDoubleTapTimeout() source) to decide between single and double tap.

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

Citrate answered 6/1, 2012 at 22:27 Comment(0)
S
4

GuestureDetecter Works Well on Most Devices, I would like to know how the time between two clicks can be customized on double click event, i wasn't able to do that. I updated the above code by "Bughi" "DoubleClickListner", added a timer using handler that executes a code after a specific delay on single click, and if double click is performed before that delay it cancels the timer and single click task and only execute double click task. Code is working Fine Makes it perfect to use as double click listner:

  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

        }
    });
Shortstop answered 10/10, 2014 at 10:14 Comment(1)
good job,but all the timer code really belongs in the onClick() method of the DoubleClickListener class class so as to not repeat it everywhereLasonde
F
3
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();
                    }
                }

            }
        });
Foliaceous answered 27/10, 2015 at 15:21 Comment(0)
W
3

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();
}
Weixel answered 3/1, 2018 at 21:5 Comment(1)
Nice and simple, thanks!Applause
C
2

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;
    }
}
Cuttie answered 2/9, 2015 at 8:19 Comment(0)
E
2

If you are using Kotlin then you can do it like this:

I spend a lot of time to convert this code to Kotlin hope it save someone's time

Create a gesture detector:

      val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
            override fun onDoubleTap(e: MotionEvent): Boolean {

                Toast.makeText(this@DemoActivity,"Double Tap",Toast.LENGTH_LONG).show()

                //Show or hide Ip address on double tap
                toggleIPaddressVisibility()

                return true;
            }

            override fun onLongPress(e: MotionEvent) {
                super.onLongPress(e);

                //rotate frame on long press
                toggleFrameRotation()

                Toast.makeText(this@DemoActivity,"LongClick",Toast.LENGTH_LONG).show()
            }

            override fun onDoubleTapEvent(e: MotionEvent): Boolean {
                return true
            }

            override fun onDown(e: MotionEvent): Boolean {
                return true
            }
        })

Assign to any of your view:

        IPAddress.setOnTouchListener { v, event ->
          return@setOnTouchListener  gestureDetector.onTouchEvent(event)
        }
Ejaculate answered 12/3, 2019 at 9:47 Comment(1)
Works Fine With KotlinLankton
A
2

This is a solution that wait if there is a second clic before executing any action

  int init = 0;
  myView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {


            if (init == 0) {
                init++;
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {



                        if (init == 1) {
                            Log.d("hereGoes", "actionOne");
                        } else {
                            Log.d("hereGoes", "actionTwo");
                        }


                        init = 0;
                    }
                }, 250);
            } else {
                init++;
            }

        }
    });
Adapter answered 9/8, 2019 at 12:8 Comment(0)
E
1

Realization single and double click

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

}

Eaglet answered 24/2, 2016 at 13:45 Comment(0)
R
1
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;
    }
}
}
Reviere answered 9/6, 2016 at 12:38 Comment(0)
P
1

Thread + Interface = DoubleTapListener, AnyTap listener etc

In this example, I have implemented the DoubleTap Listener with a Thread. You can add my listener with any View object as you do with any ClickListener. Using this approach you can easily pull off any kind of click listener.

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) Listener Callback

public interface DoubleTapCallback {

      public void onDoubleClick(View v);

}

3) Implement in your Activity

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

Poppy answered 26/1, 2019 at 15:41 Comment(0)
B
1

you can implement double-tap using the GestureDetectorCompat class. in this sample when double-tap on textview you can do your logic.

public class MainActivity extends AppCompatActivity {

GestureDetectorCompat gestureDetectorCompat;
TextView textElement;

@Override
protected void onCreate(Bundle savedInstanceState) { 
    .....
    textElement = findViewById(R.id.textElement);
    gestureDetectorCompat = new GestureDetectorCompat(this, new MyGesture());
    textElement.setOnTouchListener(onTouchListener);

}

 View.OnTouchListener onTouchListener = new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        gestureDetectorCompat.onTouchEvent(event);
        return true;
    }
};
    class MyGesture extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        // whatever on double click
        return true;
    }
}
Berte answered 21/2, 2019 at 19:42 Comment(1)
This is what I use and it's working fine. Can it be configured though? Some sensitivity factor? To set how much time between the pressing would trigger double-tap callback ?Intelsat
C
0

Solution by bughi & 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);}
Cella answered 16/6, 2016 at 11:26 Comment(0)
C
0

Equivalent C# code which i used to implement same functionality and can even customize to accept N number of Taps

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 Above code accepts 7 as number of taps before it raises the View Tapped event. But it can be customized with any number

Clarion answered 30/1, 2018 at 4:8 Comment(0)
N
0

I have implemented a simple custom method using kotlin coroutines (for java 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
   }
}
Nutty answered 11/12, 2018 at 9:24 Comment(0)
T
0

A simple way to do this in Kotlin:

button.setOnTouchListener(object : View.OnTouchListener{
    val gestureDetector = GestureDetector(object : GestureDetector.SimpleOnGestureListener(){
        override fun onDoubleTap(e: MotionEvent?): Boolean {
           //do something here
            return super.onDoubleTap(e)
        }
    })

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        //do something here
        gestureDetector.onTouchEvent(event)
        return true
    }
})
Transform answered 16/7, 2020 at 16:38 Comment(0)
A
0

To detect the type of gesture tap one can implement something inline with this (here projectText is an EditText):

projectText.setOnTouchListener(new View.OnTouchListener() {
    private GestureDetector gestureDetector = new GestureDetector(activity, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            projectText.setInputType(InputType.TYPE_CLASS_TEXT);
            activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
            return super.onDoubleTap(e);
        }
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
                projectText.setInputType(InputType.TYPE_NULL); // disable soft input
                final int itemPosition = getLayoutPosition();
                if(!projects.get(itemPosition).getProjectId().equals("-1"))
                    listener.selectedClick(projects.get(itemPosition));

            return super.onSingleTapUp(e);
        }
    });

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        gestureDetector.onTouchEvent(event);
        return false; //true stops propagation of the event
    }
});
Alforja answered 8/11, 2020 at 22:32 Comment(0)
U
0

I created a simple library to handle this. it can also detect more than two clicks (it all depends on you). after you import the ClickCounter class, here is how you use it to detect single and multiple clicks:

ClickCounter counter = new ClickCounter();


view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        counter.addClick();  // submits click to be counted
    }
});

counter.setClickCountListener(new ClickCounter.ClickCountListener() {
    @Override
    public void onClickingCompleted(int clickCount) {
        rewardUserWithClicks(clickCount); // Thats All!!😃
    }
}); 
Unpeople answered 15/12, 2020 at 9:35 Comment(0)
G
0

In Kotlin you can try this,

like i am using cardview for clicking,

(Example : on double click i perform like and dislike.)

cardviewPostCard.setOnClickListener(object : DoubleClickListener() {
    override fun onDoubleClick(v: View?) {

        if (holder.toggleButtonLike.isChecked) {
            holder.toggleButtonLike.setChecked(false) //
        } else {
            holder.toggleButtonLike.setChecked(true)

        }
    }
})

and here is your DoubleClickListener class,

abstract class DoubleClickListener : View.OnClickListener {
    var lastClickTime: Long = 0
    override fun onClick(v: View?) {
        val clickTime = System.currentTimeMillis()
        if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
            onDoubleClick(v)
        }
        lastClickTime = clickTime
    }

    abstract fun onDoubleClick(v: View?)

    companion object {
        private const val DOUBLE_CLICK_TIME_DELTA: Long = 300 //milliseconds
    }
}
Gumption answered 7/1, 2021 at 10:19 Comment(0)
P
0

While I liked the simplicity of the approach in the original answer

Here is my version

public abstract class OnDoubleClickListener implements View.OnClickListener {

    private static final int TIME_OUT = ViewConfiguration.getDoubleTapTimeout();
    private TapHandler tapHandler = new TapHandler();

    public abstract void onSingleClick(View v);
    public abstract void onDoubleClick(View v);

    @Override
    public void onClick(View v) {
        tapHandler.cancelSingleTap(v);
        if (tapHandler.isDoubleTap()){
            onDoubleClick(v);
        } else {
            tapHandler.performSingleTap(v);
        }
    }

    private class TapHandler implements Runnable {
        public boolean isDoubleTap() {
            final long tapTime = System.currentTimeMillis();
            boolean doubleTap = tapTime - lastTapTime < TIME_OUT;
            lastTapTime = tapTime;
            return doubleTap;
        }
        public void performSingleTap(View v) {
            view = v;
            v.postDelayed(this, TIME_OUT);
        }
        public void cancelSingleTap(View v) {
            view = null;
            v.removeCallbacks(this);
        }

        @Override
        public void run() {
            if (view != null) {
                onSingleClick(view);
            }
        }
        private View view;
        private long lastTapTime = 0;
    }
}

Usage is same as the original

view.setOnClickListener(new OnDoubleClickListener() {

    @Override
    public void onSingleClick(View v) {

    }

    @Override
    public void onDoubleClick(View v) {

    }
});
Presumptuous answered 14/1, 2021 at 5:7 Comment(0)
D
0

This is the Kotlin extension function version:

fun View.setOnDoubleTapListener(action: () -> Unit) {

    // instantiate GestureDetectorCompat
    val gDetector = GestureDetectorCompat(
        this.context,
        GestureDetector.SimpleOnGestureListener()
    )

    // Create anonymous class extend OnTouchListener and SimpleOnGestureListener
    val touchListener =
        object : View.OnTouchListener, GestureDetector.SimpleOnGestureListener() {
            override fun onTouch(view: View?, event: MotionEvent?): Boolean {

                gDetector.onTouchEvent(event)
                gDetector.setOnDoubleTapListener(this)

                return true
            }

            override fun onDoubleTap(e: MotionEvent?): Boolean {
                action()
                return true
            }
        }

    this.setOnTouchListener(touchListener)

}
Diaphanous answered 29/3, 2022 at 12:8 Comment(0)
E
0

Kotlin w/o extra class

override fun onCreate(savedInstanceState: Bundle?) {
// ...

        var listview: ListView = findViewById(R.id.<your_listview_id>)
        var itemLastClickTime: Long = 0
        var singleClickHandler: Handler = Handler(Looper.getMainLooper())

        // ListView item 'double click' and 'single click'
        listview.setOnItemClickListener(OnItemClickListener { av, iv, pos, id ->
            // if double click occurs, disable single click handler
            if (System.currentTimeMillis() - itemLastClickTime < 300) {
                singleClickHandler.removeCallbacksAndMessages(null)
                // do double click handling
                return@OnItemClickListener
            }
            // store the time of the item's click event
            itemLastClickTime = System.currentTimeMillis()
            // single click handler only fires, if no double click occured
            singleClickHandler.postDelayed({
                // do single click handling
            }, 300)
        })

// ...      
}       

Java w/o extra class

@Override
protected void onCreate(Bundle savedInstanceState) {
// ...

    ListView listview = findViewById(R.id.<your_listview_id>);
    final long[] itemLastClickTime = {0};
    Handler singleClickHandler = new Handler();

    listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> av, View iv, int pos, long id) {
            // if double click occurs, disable single click handler
            if (System.currentTimeMillis() - itemLastClickTime[0] < 300) {
                singleClickHandler.removeCallbacksAndMessages(null);
                // do double click handling
                return;
            }
            // store the time of the item's click event
            itemLastClickTime[0] = System.currentTimeMillis();
            // single click handler only fires, if no double click occured
            singleClickHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    // do single click handling
                }
            }, 300);
        }
    });

// ...      
}       
Epimorphosis answered 2/11, 2022 at 11:52 Comment(0)
C
0

Just listen the time of previous click. You can use this class for double click listening.

abstract class DoubleClickListener : View.OnClickListener {
    private var lastClickTime: Long = 0
    override fun onClick(v: View) {
        val clickTime = System.currentTimeMillis()
        if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
            onDoubleClick(v)
            lastClickTime = 0
        }
        lastClickTime = clickTime
    }
    abstract fun onDoubleClick(v: View)
    companion object {
        private const val DOUBLE_CLICK_TIME_DELTA: Long = 300 //milliseconds
    }
}

And, you can use it like this:

yourView.setOnClickListener(object : DoubleClickListener() {
                    override fun onDoubleClick(v: View) {
                        // do to
                    }
                })
 
Closeup answered 29/3, 2023 at 18:55 Comment(0)
L
0

Add this class:

class DoubleTapListener(context: Context, doubleTapped: () -> Unit) : View.OnTouchListener {
    private val gestureDetector: GestureDetector

    init {
        gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
            override fun onDown(e: MotionEvent): Boolean {
                return true
            }

            override fun onDoubleTap(e: MotionEvent): Boolean {
                doubleTapped()

                return true
            }
        })
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event)
    }
}

And then inside any View you want to listen to double taps, add this line:

setOnTouchListener(DoubleTapListener(context) {
    // Execute logic
})
Lolanthe answered 20/6, 2023 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.