Android:如何检测双击?

ala*_*lan 61 android listview double-click

我实现双击时遇到问题.好吧,我实现了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;
 }
Run Code Online (Sandbox Code Playgroud)

哦,另一个问题,如果我ListView有一个onItemClickListener,可以在单击或双击之间进行安卓检测吗?

Han*_*sen 107

您可以使用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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以覆盖侦听器的其他方法以获得单击,flinges等.

  • @alan你能把这个标记为答案吗?这篇文章确实回答了这个问题,对未来的googlers有用,专门研究如何实现双击.在双击时使用长按并不总是一种选择. (20认同)
  • 好的,重新阅读你的帖子后,我猜你的问题是,你没有在onDown方法中返回true.在onTouch方法中调用gestureDetectors onTouch. (3认同)
  • @Nepster否,不推荐使用,只有两个构造函数. (3认同)

bug*_*ghi 60

作为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);
}
Run Code Online (Sandbox Code Playgroud)

例:

    view.setOnClickListener(new DoubleClickListener() {

        @Override
        public void onSingleClick(View v) {

        }

        @Override
        public void onDoubleClick(View v) {

        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 这不适用于单击.您应该注册一些计时器(例如使用"处理程序"),以便在三角形内没有发生第二次单击时执行单击. (9认同)
  • 我知道了.逻辑上,它们应该是独占的,即onSingleClick()被调用或onDoubleClick()这样做,所以你应该等待决定调用哪一个以避免以后的错误.这是Android处理长按的方式(请参阅View类中的onTouchEvent(event)方法).此外,你的delta是如此之高,200ms更合理.通常在PC上,我认为没有人注意到双击延迟. (2认同)

Dav*_*ebb 22

你为什么不使用长按?或者你已经在使用其他东西?双击长按的优点:

  • 长按是UI指南中的推荐交互,Double Touch不是.
  • 这是用户期望的; 用户可能找不到Double Touch操作,因为他们不会去寻找它
  • 它已在API中处理.
  • 实施双击将影响单次触摸的处理,因为您必须等待,以便在处理之前查看每个Single Touch是否变为双触摸.

  • 双击侦听器的一个很好的例子是视频视图,使其全屏查看。 (2认同)

M.H*_*fny 14

将"Bu​​ghi""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);
}
Run Code Online (Sandbox Code Playgroud)

并可以称为:

view.setOnClickListener(new DoubleClickListener() {

            @Override
            public void onSingleClick(View v) {

            }

            @Override
            public void onDoubleClick(View v) {

            }
        });
Run Code Online (Sandbox Code Playgroud)


Nay*_*pte 7

如果您不想进入自定义视图,则可以使用以下方法.例如ImageView

// class level

GestureDetector gestureDetector;
boolean tapped;
ImageView imageView;

// inside onCreate of Activity or Fragment
gestureDetector = new GestureDetector(context,new GestureListener());
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

对于ImageView

imageView.setOnTouchListener(new OnTouchListener() {

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

    });
Run Code Online (Sandbox Code Playgroud)


Sur*_*gch 5

双击单击

仅双击

SimpleOnGestureListener通过使用(如Hannes Niederhausen 的回答所示) ,可以很容易地检测视图上的双击。

private class GestureListener extends GestureDetector.SimpleOnGestureListener {

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

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

我看不出重新发明逻辑有什么大的优势(就像bughi 的答案)。

双击和单击(带延迟)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

双击和单击无延迟

潜在的问题onSingleTapConfirmed是延迟。有时明显的延迟是不可接受的。在这种情况下,您可以替换onSingleTapConfirmedonSingleTapUp.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

不过,您需要意识到,如果双击,则都会被调用。 (这本质上就是bughi 的答案所做的,也是一些评论者抱怨的。)您要么需要使用延迟,要么调用这两种方法。不可能实现无延迟的单击,同时知道用户是否要再次点击。onSingleTapUp onDoubleTap

如果单击延迟对您来说不可接受,那么您有以下几种选择:

  • 接受 和onSingleTapUponDoubleTap将被要求双击。只要适当划分你的逻辑,这样就不重要了。这基本上就是我在自定义键盘上实现双击大写锁定时所做的事情。
  • 不要使用双击。对于大多数事情来说,这不是一个直观的 UI 操作。正如戴夫·韦伯(Dave Webb)所说,长按可能更好。您还可以使用以下方法来实现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) {
    
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)