【问题标题】:Can't handle both click and touch events simultaneously不能同时处理点击和触摸事件
【发布时间】:2011-07-06 18:32:54
【问题描述】:

我正在尝试处理按钮上的触摸事件和单击事件。我执行以下操作:

button.setOnClickListener(clickListener);
button.setOnTouchListener(touchListener);

当注册任何一个监听器时,一切正常,但当我尝试同时使用它们时,只会触发触摸事件。任何解决方法?我做错了什么?

【问题讨论】:

    标签: android listeners ontouchlistener


    【解决方案1】:

    这有点棘手。

    如果您设置了onTouchListener,则需要在ACTION_DOWN 中返回true,以告诉系统我已经消费了该事件并且它不会传播到其他侦听器。

    但是OnClickListener 不会被解雇。

    所以你可能会想,我会在那里做我的事并返回false,这样我也可以获得点击。 如果你这样做,它会起作用,但你不会订阅其他即将到来的触摸事件(ACTION_MOVEACTION_UP) 因此,唯一的选择是在那里返回true,但是你不会像我们之前所说的那样收到任何点击事件。

    所以你需要在ACTION_UPview.performClick()中手动执行点击

    这会起作用。

    【讨论】:

    • 这里的关键部分是“您需要在ACTION_UP中手动执行点击”,谢谢先生
    • 这确实是最好的答案。在我的 ACTION_MOVE 中,如果满足某个 deltaX 阈值,我将开始拦截触摸事件。如果我达到 ACTION_UP 并且从未达到 deltaX 阈值,我会触发 listView.performClick(),否则,我会执行滑动操作。
    • 这显然应该是答案。
    • 很好的答案。坚持下去
    【解决方案2】:

    ClickListenerTouchListener 之间存在细微但非常重要的区别。 TouchListener 在视图可以响应事件之前执行。 ClickListener 只有在视图处理完事件后才会收到它的事件。

    因此,当您触摸屏幕时,首先执行 TouchListener,当您为您的事件返回 true 时,ClickListener 将永远不会得到它。但是如果你按下设备的轨迹球,ClickListener 应该会被触发,因为TouchListener 不会响应它。

    【讨论】:

      【解决方案3】:

      感谢@urSus 的精彩回答
      但在那种情况下,每次触摸都会执行点击,即使是 ACTION_MOVE
      假设您想将 move 事件和 click 事件分开,您可以使用一个小技巧
      定义一个boolean 字段并像这样使用:

       @Override
              public boolean onTouch(View view, MotionEvent motionEvent)
              {
                  switch (motionEvent.getAction() & MotionEvent.ACTION_MASK)
                  {
                      case MotionEvent.ACTION_DOWN:
                          shouldClick = true;
                          .
                          .
                          break;
                      case MotionEvent.ACTION_UP:
                          if (shouldClick)
                              view.performClick();
                          break;
                      case MotionEvent.ACTION_POINTER_DOWN:
                          break;
                      case MotionEvent.ACTION_POINTER_UP:
                          break;
                      case MotionEvent.ACTION_MOVE:
                          //Do your stuff
                          shouldClick = false;
                          break;
                  }
                  rootLayout.invalidate();
                  return true;
              }
      

      【讨论】:

      • 很好的答案。坚持下去
      • 执行点击是查看方式吗?或者我们必须执行点击的自定义方法
      • @user3475052 performClick() 是每个视图及其子类方法
      • 有效,如果 return true; 在 MotionEvent.ACTION_DOWN 分支中,移除 MotionEvent.ACTION_MOVE 分支,在 MotionEvent.ACTION_UP 分支中获取运动差异。见stackoverflow.com/questions/17831395/…
      • 真的这个答案在所有类似问题中都是最好的
      【解决方案4】:

      您应该在 OnTouchListener 中返回 false,然后您的 OnClickListener 也将被处理。

      【讨论】:

        【解决方案5】:

        我想你在OnTouchListener 中返回true?这将消耗事件,因此它不会被发送到任何进一步的处理。

        附带说明 - 同时拥有点击和触摸侦听器有什么意义?

        【讨论】:

        • 客户端希望在按下和释放按钮时播放声音,我在“ACTION_UP”和“ACTION_DOWN”事件中这样做。点击事件执行逻辑。现在我必须处理触摸事件本身的逻辑。
        • 一个可以左右拖动,完成后点击的按钮?
        【解决方案6】:

        以上所有答案都是说我们不能同时处理setOnTouchListenersetOnClickListener
        但是,我看到我们可以通过return falsesetOnTouchListener处理两者

        示例

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        
            button = findViewById(R.id.button)
            button.setOnClickListener {
                Log.i("TAG", "onClick")
            }
        
            button.setOnTouchListener { v, event ->
                Log.i("TAG", "onTouch " + event.action)
                false
            }
        }
        

        当我点击Button时,logcat会显示如下

        I/TAG: onTouch 0
        I/TAG: onTouch 1
        I/TAG: onClick
        

        【讨论】:

        • 从onTouch返回false等于根本不设置onTouchListener。
        【解决方案7】:

        这是一个不影响 ClickListener 的 TouchListener 示例。

        import android.graphics.PointF
        import android.view.MotionEvent
        import android.view.MotionEvent.*
        import android.view.View
        import kotlin.math.abs
        
        object TouchAndClickListener : View.OnTouchListener {
        
            /** Those are factors you can change as you prefer */
            private const val touchMoveFactor = 10
            private const val touchTimeFactor = 200
        
        
            private var actionDownPoint = PointF(0f, 0f)
            private var previousPoint = PointF(0f, 0f)
            private var touchDownTime = 0L
        
            override fun onTouch(v: View, event: MotionEvent) = when (event.action) {
                ACTION_DOWN -> PointF(event.x, event.y).let {
        
                    actionDownPoint = it  // Store it to compare position when ACTION_UP
                    previousPoint = it  // Store it to compare position when ACTION_MOVE
                    touchDownTime = now() // Store it to compare time when ACTION_UP
        
                    /* Do other stuff related to ACTION_DOWN you may whant here */
        
                    true
                }
        
                ACTION_UP -> PointF(event.x, event.y).let {
        
                    val isTouchDuration = now() - touchDownTime < touchTimeFactor  // short time should mean this is a click
                    val isTouchLength = abs(it.x - actionDownPoint.x) + abs(it.y - actionDownPoint.y) < touchMoveFactor  // short length should mean this is a click
        
                    val shouldClick = isTouchLength && isTouchDuration  // Check both
        
                    if (shouldClick) yourView.performClick() //Previously define setOnClickListener{ } on yourView, then performClick() will call it
        
                    /* Do other stuff related to ACTION_UP you may whant here */
        
                    true
                }
        
                ACTION_MOVE -> PointF(event.x, event.y).let {
        
                    /* Do other stuff related to ACTION_MOVE you may whant here */
        
                    previousPoint = it
                    true
                }
        
                else -> false // Nothing particular with other event
            }
        
            private fun now() = System.currentTimeMillis()
        }
        

        【讨论】:

        • 很好的答案,但请它应该在 java 而不是 kotlin 中
        【解决方案8】:

        感谢@Nicolas Duponchel,这就是我实现 onClick 和 onTouch 事件的方式

          //Define these globally e.g in your MainActivity class
          private short touchMoveFactor = 10;
          private short touchTimeFactor = 200;
          private PointF actionDownPoint = new PointF(0f, 0f);
          private long touchDownTime = 0L;
                    
                     @Override
                        public boolean onTouchEvent(MotionEvent event) {
                            final int action = event.getAction();
                            switch (action) {
                             case MotionEvent.ACTION_DOWN: {
                                    actionDownPoint.x = event.getX();
                                    actionDownPoint.y = event.getY();
                                    touchDownTime = System.currentTimeMillis();
                                    break;
                                    }
                             case MotionEvent.ACTION_UP: {
                                    //on touch released, check if the finger was still close to the point he/she clicked 
                                    boolean isTouchLength = (Math.abs(event.getX() - actionDownPoint.x)+ Math.abs(event.getY() - actionDownPoint.y)) < touchMoveFactor;
                                    boolean isClickTime = System.currentTimeMillis() - touchDownTime < touchTimeFactor;
                
                                    //if it's been more than 200ms since user first touched and, the finger was close to the same place when released, consider it a click event
                                    //Please note that this is a workaround :D
                                    if (isTouchLength && isClickTime){ 
                                       //call this method on the view, e.g ivPic.performClick();
                                       performClick();}
                                    break;
                                    }
                             }
                        }
        

        【讨论】:

          【解决方案9】:
          button.setOnTouchListener(this);
          

          实现接口和代码在这里:

          @Override
          public boolean onTouch(View view, MotionEvent motionEvent) {
              switch (view.getId()) {
                  case R.id.send:
                      switch(motionEvent.getAction()){
                          case MotionEvent.ACTION_DOWN:
                              //when the user has pressed the button
                              //do the needful here
                              break;
                          case MotionEvent.ACTION_UP:
                              //when the user releases the button
                              //do the needful here
                              break;
                      }
                      break;
              }
              return false;
          }
          

          【讨论】:

            【解决方案10】:

            要使这两个事件在 gridview 中成为可能,只能通过如下方式使触摸侦听器返回“false”,这对我有用。

            **GridView myView = findViewById(R.id.grid_view);
            myView.setOnTouchListener(new OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {
                    // ... Respond to touch events
                    return false;
                }
            });**
            

            这样两个事件都可以实现

            【讨论】:

              【解决方案11】:
              ## Exact working solution for both click action and touch listener(dragging) ##
              
              private int initialX;
              private int initialY;
              private float initialTouchX;
              private float initialTouchY;
              private float CLICK_ACTION_THRESHOLD = 0.5f;
              private float startX;
              private float startY;
              
               @Override
              public boolean onTouch(View view, MotionEvent event) {
                  switch (view.getId()) {
                      case R.id.chat_head_profile_iv:
                          switch (event.getAction()) {
                              case MotionEvent.ACTION_DOWN:
                                  //remember the initial position.
                                  initialX = params.x;
                                  initialY = params.y;
                                  startX = event.getX();
                                  startY = event.getY();
                                  //get the touch location
                                  initialTouchX = event.getRawX();
                                  initialTouchY = event.getRawY();
                                  return true;
                              case MotionEvent.ACTION_UP:
                                  float endX = event.getX();
                                  float endY = event.getY();
                                  if (shouldClickActionWork(startX, endX, startY, endY)) {
                                      openScreen();// WE HAVE A CLICK!!
                                  }
                                  return true;
                              case MotionEvent.ACTION_MOVE:
                                  //Calculate the X and Y coordinates of the view.
                                  params.x = initialX + (int) (event.getRawX() - initialTouchX);
                                  params.y = initialY + (int) (event.getRawY() - initialTouchY);
              
                                  //Update the layout with new X & Y coordinate
                                  mWindowManager.updateViewLayout(mChatHeadView, params);
                                  return true;
                          }
                          break;
                  }
                  return true;
              }
              
              private boolean shouldClickActionWork(float startX, float endX, float startY, float endY) {
                  float differenceX = Math.abs(startX - endX);
                  float differenceY = Math.abs(startY - endY);
                  if ((CLICK_ACTION_THRESHOLD > differenceX) && (CLICK_ACTION_THRESHOLD > differenceY))
                      return true;
                  else
                      return false;
              }
              

              【讨论】:

              • 什么是参数?
              【解决方案12】:

              ACTION_UP 中使用条件手动执行onClick

              boolean shouldClick = event.eventTime - event.downTime <= 200 // compares the time with some threshold
              

              所以,试试MotionEvent.ACTION_UP

              if(event.eventTime - event.downTime <= 200) { // case or when statement of action Touch listener
                  view.performClick();
              }
              

              【讨论】:

                【解决方案13】:

                我知道为时已晚,但如果有人为此苦苦寻找干净的解决方案,那就是。

                这些用于测量触摸和移开手指之间的时间。

                    private long clickTime = 0;
                    public static final long CLICK_TIMEOUT = 200; // 200ms
                

                这是我的onTouchListner。像魅力一样工作

                    private final View.OnTouchListener onTouchListener = (v, event) -> {
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        clickTime = System.currentTimeMillis();
                        return true;
                    } else if(event.getAction() ==  MotionEvent.ACTION_UP) {
                        if(System.currentTimeMillis()-clickTime < Constants.CLICK_TIMEOUT)
                        {
                            Toast.makeText(getContext(), "clicked", Toast.LENGTH_SHORT).show();
                            return true;
                        }
                        return false;
                    }
                    else if(event.getAction() == MotionEvent.ACTION_MOVE){
                        if(System.currentTimeMillis()-clickTime > Constants.CLICK_TIMEOUT)
                        {
                            ClipData data = ClipData.newPlainText("" , "");
                            View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
                            v.startDrag(data , shadowBuilder , v , 0);
                            return false;
                        }
                        return false;
                    }
                    return false;
                };
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-03-28
                  • 2023-03-27
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-06-08
                  相关资源
                  最近更新 更多