Android:如何检测双击?

Posted

技术标签:

【中文标题】Android:如何检测双击?【英文标题】:Android: How to detect double-tap? 【发布时间】:2011-01-14 03:08:45 【问题描述】:

我在执行双击时遇到问题。好吧,我实现了onGestureListenergestureDetector,但我不确定问题出在哪里,这是我的代码:

 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 有一个onItemClickListenerandroid 可以检测到它是单击还是双击?

【问题讨论】:

标题问题具有误导性。谁会想要摧毁一个机器人? 【参考方案1】:

您可以使用 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;
        
    

您可以覆盖侦听器的其他方法以获取单击、弹跳等。

【讨论】:

好的,重新阅读您的帖子后,我想您的问题是,您在 onDown 方法中没有返回 true。手势检测器 onTouch 在你的 onTouch 方法中被调用。 @alan 你能把这个标记为答案吗?这篇文章确实回答了这个问题,并且对未来专门寻找如何实现双击的谷歌用户很有用。长按而不是双击并不总是一种选择。 gestureDetector 被贬低 @Nepster 不,没有被弃用,只有两个构造函数被弃用。 有关更多详细信息和更多示例,请查看这个投票率很高的答案:***.com/a/26529756/4143245【参考方案2】:

作为 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) 

        
    );

【讨论】:

这不适用于单击。您应该注册一些计时器(例如使用Handler)以在增量内没有发生第二次点击时执行单击。 它确实适用于单击。执行双击时,您会调用 onSingleClick() 然后调用 onDoubleClick() 这对我的用例来说很好。添加计时器会在 onDoubleClick() 之前删除对 onSingleClick() 的调用,但在我的情况下,在触发事件之前有延迟是不可接受的 我明白了。从逻辑上讲,它们应该是独占的,即要么 onSingleClick() 被调用,要么 onDoubleClick() 被调用,所以你应该等待决定调用哪一个以避免以后的错误。这是 Android 处理长按的方式(参见 View 类中的 onTouchEvent(event) 方法)。此外,您的增量如此之高,200 毫秒更合理。通常在 PC 上,我认为没有人注意到双击延迟。 这个对我有用。我的代码设置为只能使用onTouchListener,并且我需要检测双击。但是,以它为基础并稍加修改,我就让它工作了。感谢这个好主意! 这是唯一适用于我的解决方案..我尝试了 6-7 种方法,但没有任何效果,但这是 +10 解决方案..【参考方案3】:

你为什么不使用长按?还是您已经将其用于其他用途?长按比双击的优势:

长按是 UI 指南中推荐的交互方式,而不是双击。 这是用户所期望的;用户可能不会找到双击操作,因为他们不会去寻找它 已经是handled in the API。 实现双击会影响对单点触摸的处理,因为您必须等待查看每个单点触摸是否会变成双点触摸,然后才能对其进行处理。

【讨论】:

嗯,我正在尝试实现类似海豚浏览器的隐藏和显示选项卡的操作(由于空间限制)......我已经为我的列表视图实现了长按......所以我会需要双击 很公平,我只是想确保您没有忘记长按。 双击监听器的一个很好的例子是视频视图,使其全屏查看。 顺便说一句,长按不再总是优于安卓上的双击;两者都是推荐的手势,取决于常用的用法:google.com/design/spec/patterns/gestures.html# @ToolmakerSteve 我只将双击用作即时缩放操作。长按可以选择项目的地方,启动拖动。 Here is new link Material design 中的手势。【参考方案4】:

将“Bughi”“DoubleClickListner”和“Jayant Arora”定时器组合到一个包含的类中:

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) 

            
        );

【讨论】:

Handler is better than TimerTask。使用handler.postDelayed() 如果您使用onSingleTapConfirmed(),则无需为单击延迟编写所有这些逻辑。见this answer。【参考方案5】:

如果您不想使用自定义视图,则可以使用以下方法。 例如图像视图

// 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.setOnTouchListener(new OnTouchListener() 

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

    );

【讨论】:

【参考方案6】:

这是我的解决方案,它使用默认的setOnItemClickListener()。我有同样的任务要执行。很快我将在我的 github 上发布示例和自定义类。 给出简要说明。我不确定以毫秒为单位的时间是否适合系统(请参阅ViewConfiguration.getDoubleTapTimeout() 来源)在单击和双击之间做出决定。

编辑: 在这里看到它: https://github.com/NikolaDespotoski/DoubleTapListView 或 https://github.com/NikolaDespotoski/DoubleTapListViewHandler

【讨论】:

【参考方案7】:

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

        
    );

【讨论】:

干得好,但所有定时器代码确实属于DoubleClickListener类的onClick()方法,以免到处重复【参考方案8】:
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();
                    
                

            
        );

【讨论】:

【参考方案9】:

双击单击

仅双击

使用SimpleOnGestureListener 很容易检测到视图上的双击(如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;
    

我看不出为此重新发明逻辑有多大优势(例如bughi's answer)。

有延迟的双击和单击

您还可以使用SimpleOnGestureListener 将单击和双击区分为互斥事件。为此,您只需覆盖onSingleTapConfirmed。这将延迟运行单击代码,直到系统确定用户没有双击(即延迟>ViewConfiguration.getDoubleTapTimeout())。绝对没有理由为此重新发明所有逻辑(就像在this、this 和其他答案中所做的那样)。

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;
    

不过,您需要意识到 both onSingleTapUp onDoubleTap 在双击时会被调用。 (这本质上是bughi's answer 所做的以及一些评论者所抱怨的。)您要么需要使用延迟,要么同时调用这两种方法。没有延迟的单击是不可能的,同时知道用户是否要再次点击。

如果您不能接受单击延迟,那么您有几个选择:

接受将调用onSingleTapUponDoubleTap 进行双击。只需适当地划分你的逻辑,这样就没有关系了。这基本上就是我在自定义键盘上实现双击大写锁定时所做的。

不要使用双击。对于大多数事情来说,这不是一个直观的 UI 操作。作为Dave Webb suggests,长按可能更好。您也可以使用 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) 

    

【讨论】:

【参考方案10】:

我的解决方案,可能会有所帮助。

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;
    

【讨论】:

【参考方案11】:

即兴 dhruvi 代码

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

【讨论】:

【参考方案12】:

如果您使用的是 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)
        

【讨论】:

与 Kotlin 配合得很好【参考方案13】:

这是在执行任何操作之前等待是否有第二个 clic 的解决方案

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

        
    );

【讨论】:

【参考方案14】:

实现单双击

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

【讨论】:

【参考方案15】:
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;
    


【讨论】:

【参考方案16】:

线程 + 接口 = DoubleTapListener、AnyTap 监听器等

在这个例子中,我用一个线程实现了 DoubleTap 监听器。 您可以像使用任何 ClickListener 一样将我的侦听器添加到任何 View 对象中。 使用这种方法,您可以轻松地关闭任何类型的点击侦听器。

yourButton.setOnClickListener(new DoubleTapListener(this));

1) 我的监听类

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) 在您的 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+""); 

相关链接:

你可以看到完整的工作代码HERE

【讨论】:

【参考方案17】:

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

【讨论】:

【参考方案18】:

我用来实现相同功能的等效 C# 代码,甚至可以自定义以接受 N 个 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;
        
    

目前,上述代码在引发 View Tapped 事件之前接受 7 作为点击次数。 但它可以定制任何数字

【讨论】:

【参考方案19】:

我已经使用kotlin coroutines 实现了一个简单的自定义方法(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
   

【讨论】:

【参考方案20】:

您可以使用GestureDetectorCompat 类实现双击。 在此示例中,当双击 textview 时,您可以执行您的逻辑。

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;
    

【讨论】:

这是我使用的,它工作正常。不过可以配置吗?一些敏感因素?设置按下之间的时间会触发双击回调?【参考方案21】:

我创建了一个简单的库来处理这个问题。它还可以检测到两次以上的点击(这完全取决于您)。导入 ClickCounter class 后,您可以使用它来检测单次和多次点击:

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

【讨论】:

【参考方案22】:

在 Kotlin 你可以试试这个,

就像我使用 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
    

【讨论】:

【参考方案23】:

虽然我喜欢原始答案中方法的简单性

这是我的版本

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) 

    
);

【讨论】:

【参考方案24】:

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

【讨论】:

【参考方案25】:

要检测手势点击的类型,可以实现一些内联的东西 (这里projectTextEditText):

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

【讨论】:

以上是关于Android:如何检测双击?的主要内容,如果未能解决你的问题,请参考以下文章

Android使用GestureDetector进行手势检测

Android使用GestureDetector进行手势检测

Android使用GestureDetector进行手势检测

在视频视图中检测双击(双击)或长按

如何在 android 复选框检查更改上接收事件?

android button 双击操作如何监听