Android: как обнаружить двойной тап?

У меня проблема с реализацией двойного нажатия. Ну, я реализовал onGestureListener и у меня был gestureDetector, но я не уверен, в чем проблема, вот мой код:

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

О, еще один вопрос, если мой ListView имеет onItemClickListener, Android может обнаружить между одним нажатием или двойным нажатием для него?

29 ответов

Решение

Почему вы не используете длинное нажатие? Или вы уже используете это для чего-то другого? Преимущества длительного нажатия по сравнению с двойным касанием:

  • Long Press - рекомендуемое взаимодействие в Руководстве по пользовательскому интерфейсу, Double Touch - нет.
  • Это то, что пользователи ожидают; пользователь может не найти действие Double Touch, поскольку он не будет искать его
  • Это уже обработано в API.
  • Реализация Double Touch повлияет на обработку Single Touch, потому что вам придется подождать, чтобы увидеть, превращается ли каждое Single Touch в Double Touch, прежде чем вы сможете его обработать.

Вы можете использовать GestureDetector. Смотрите следующий код:

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

Вы можете переопределить другие методы слушателя, чтобы получить одиночные нажатия, щелчки и так далее.

В качестве легкой альтернативы GestureDetector вы можете использовать этот класс

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

Пример:

    view.setOnClickListener(new DoubleClickListener() {

        @Override
        public void onSingleClick(View v) {

        }

        @Override
        public void onDoubleClick(View v) {

        }
    });

Объединение "Bughi", "DoubleClickListner" и "Jayant Arora" Timer в один содержащий класс:

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

и может называться как:

view.setOnClickListener(new DoubleClickListener() {

            @Override
            public void onSingleClick(View v) {

            }

            @Override
            public void onDoubleClick(View v) {

            }
        });

Если вы не хотите переходить к пользовательскому виду, используйте следующий подход. например, 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;
    }
}

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

для ImageView

imageView.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            return gestureDetector.onTouchEvent(event);
        }

    });

Двойное касание и одно касание

Только дважды нажмите

Это довольно легко обнаружить двойным нажатием на вид с помощью SimpleOnGestureListener (как показано в ответе Ханнеса Нидерхаузена).

private class GestureListener extends GestureDetector.SimpleOnGestureListener {

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

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

Я не вижу большого преимущества в повторном изобретении логики для этого (например, в ответе Буги).

Двойное и одно касание с задержкой

Вы также можете использовать SimpleOnGestureListener различать одно касание и двойное касание как взаимоисключающие события. Для этого просто переопределите onSingleTapConfirmed, Это задержит выполнение кода одним нажатием, пока система не будет уверена, что пользователь не нажал дважды (т. Е. Задержка> ViewConfiguration.getDoubleTapTimeout()). Определенно нет причин заново изобретать всю логику для этого (как это сделано в этом, этом и других ответах).

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

Двойное и одно касание без задержки

Потенциальная проблема с onSingleTapConfirmed задержка Иногда заметная задержка недопустима. В этом случае вы можете заменить onSingleTapConfirmed с 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;
    }
}

Вы должны понимать, однако, что оба onSingleTapUp а также onDoubleTap будет вызван, если есть двойное нажатие. (Это, по сути , ответ Буги и на что жаловались некоторые комментаторы.) Вам нужно либо использовать задержку, либо вызвать оба метода. Невозможно выполнить одно касание без задержки и в то же время узнать, собирается ли пользователь снова нажать.

Если задержка одним касанием для вас неприемлема, у вас есть несколько вариантов:

  • Примите, что оба onSingleTapUp а также onDoubleTap будет вызван для двойного нажатия. Просто разделите свою логику соответствующим образом, чтобы она не имела значения. По сути, это то, что я сделал, когда реализовал двойное касание для caps-lock на пользовательской клавиатуре.
  • Не используйте двойной тап. Это не интуитивно понятный пользовательский интерфейс для большинства вещей. Как предполагает Дэйв Уэбб, долгое нажатие, вероятно, лучше. Вы также можете реализовать это с 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) {
    
        }
    }
    

GuestureDetecter хорошо работает на большинстве устройств, я хотел бы знать, как можно настроить время между двумя щелчками на событии двойного щелчка, я не смог этого сделать. Я обновил приведенный выше код с помощью "Bughi" "DoubleClickListner", добавил таймер с помощью обработчика, который выполняет код после определенной задержки одним кликом, а если двойной щелчок выполняется до этой задержки, он отменяет задачу таймера и одиночного щелчка и выполняет только Задача двойного щелчка. Код работает отлично Делает его идеальным для использования в качестве двойного клика:

  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

        }
    });

Это мое решение, оно использует по умолчанию setOnItemClickListener(), У меня была такая же задача для реализации. Скоро я опубликую пример и пользовательский класс на моем github. Краткое объяснение дано. Я не уверен, что время в миллисекундах - это правильная разница для системы (см. ViewConfiguration.getDoubleTapTimeout() источник), чтобы выбрать между одним и двойным нажатием.

Изменить: посмотреть здесь: https://github.com/NikolaDespotoski/DoubleTapListView или https://github.com/NikolaDespotoski/DoubleTapListViewHandler

Импровизированный код Дхруви

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

            }
        });

Если вы используете Kotlin, вы можете сделать это так:

Я трачу много времени на преобразование этого кода в Kotlin, надеюсь, это сэкономит кому-то время

Создать детектор жестов:

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

Присвойте любому вашему мнению:

        IPAddress.setOnTouchListener { v, event ->
          return@setOnTouchListener  gestureDetector.onTouchEvent(event)
        }

Это решение, которое ждет, если будет второй щелчок, прежде чем выполнять какое-либо действие

  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++;
            }

        }
    });

Мое решение, может быть полезно.

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

Вы можете реализовать двойное нажатие, используя GestureDetectorCompat учебный класс. в этом примере при двойном нажатии на текстовое представление вы можете сделать свою логику.

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

Thread + Interface = DoubleTapListener, прослушиватель AnyTap и т. Д.

В этом примере я реализовал прослушиватель DoubleTap с потоком. Вы можете добавить мой слушатель с любым объектом View, как и с любым ClickListener. Используя этот подход, вы можете легко осуществить прослушивание любого вида щелчков.

yourButton.setOnClickListener(new DoubleTapListener(this));

1) Мой класс Listrener

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) Слушатель обратного вызова

public interface DoubleTapCallback {

      public void onDoubleClick(View v);
}

3) Реализуйте в своей деятельности

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+""); 
}

Соответствующая ссылка:

Вы можете увидеть полный рабочий код ЗДЕСЬ

Реализация одиночного и двойного клика

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

}

Эквивалентный код C#, который я использовал для реализации той же функциональности и даже могу настроить, чтобы принять N количество нажатий

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

В настоящее время код выше принимает 7 как количество нажатий, прежде чем он вызывает событие View Tapped. Но его можно настроить под любым номером

Простой способ сделать это в Котлине

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

Решение от Bughi & Jayant Arora для копирования:

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

Добавьте этот класс:

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

И тогда внутри любогоViewвы хотите слушать двойные нажатия, добавьте эту строку:

      setOnTouchListener(DoubleTapListener(context) {
    // Execute logic
})

Котлин без экстра класса

      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 без дополнительного класса

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

// ...      
}       

Я реализовал простой пользовательский метод, используя сопрограммы kotlin (для Java можно сделать через потоки).

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
   }
}

Я создал простую библиотеку, чтобы справиться с этим. он также может обнаруживать более двух щелчков (все зависит от вас). После импорта класса ClickCounter вот как вы его используете для обнаружения одиночных и множественных щелчков:

      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!!😃
    }
}); 

Хотя мне понравилась простота подхода в исходном ответе

Вот моя версия

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

Использование такое же, как и в оригинале

      view.setOnClickListener(new OnDoubleClickListener() {

    @Override
    public void onSingleClick(View v) {

    }

    @Override
    public void onDoubleClick(View v) {

    }
});

В Котлине вы можете попробовать это,

как будто я использую cardview для нажатия,

(Пример: при двойном щелчке я показываю, что нравится и не нравится.)

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

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

        }
    }
})

и вот ваш класс DoubleClickListener,

      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
    }
}

Это версия функции расширения Kotlin:

      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)

}

Просто послушайте время предыдущего клика. Вы можете использовать этот класс для прослушивания двойным щелчком мыши.

      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
    }
}

И вы можете использовать его следующим образом:

      yourView.setOnClickListener(object : DoubleClickListener() {
                    override fun onDoubleClick(v: View) {
                        // do to
                    }
                })
 

Чтобы определить тип касания жеста, можно реализовать что-то встроенное с этим: (здесь projectText - это 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
        }

    });
Другие вопросы по тегам