【问题标题】:LongClick event happens too quickly. How can I increase the clicktime required to trigger it?LongClick 事件发生得太快。如何增加触发它所需的点击时间?
【发布时间】:2011-12-17 13:37:37
【问题描述】:

在我正在开发的应用程序中,我要求用户必须在某个操作发生之前单击并按住组件一段时间。

我目前正在使用OnLongClickListener来监听longclick,但是我发现触发OnLongClick事件的点击时长太短了。

例如,假设 LongClick 事件在单击 400 毫秒后触发,但我希望用户必须在事件触发之前单击并按住 1200 毫秒。

有什么方法可以将 LongClick 事件配置为需要更长的点击时间?
或者是否有其他构造可以让我听到更长的点击?

【问题讨论】:

    标签: android event-handling


    【解决方案1】:

    onLongClick 事件无法更改定时器,由android自己管理。

    可以使用 .setOnTouchListener()。

    然后在 MotionEvent 为 ACTION_DOWN 时注册。
    请注意变量中的当前时间。
    然后当一个带有 ACTION_UP 的 MotionEvent 被注册并且 current_time - actionDown 时间 > 1200 毫秒时,然后做一些事情。

    差不多:

    Button button = new Button();
    long then = 0;
        button.setOnTouchListener(new OnTouchListener() {
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction() == MotionEvent.ACTION_DOWN){
                    then = (Long) System.currentTimeMillis();
                }
                else if(event.getAction() == MotionEvent.ACTION_UP){
                    if(((Long) System.currentTimeMillis() - then) > 1200){
                        return true;
                    }
                }
                return false;
            }
        })
    

    【讨论】:

    • 这几乎可以满足我的需要。发布的方法仅在用户抬起手指时执行代码。我希望我的业务逻辑在延迟结束后立即运行,而不是在用户停止按下时运行。
    • 为什么 then = getSystemTime() 被注释掉了? getSystemTime() 方法也不起作用。你的意思是:System.currentTimeMillis()
    • 已确认。然后将其定义为 long,而不是 int。然后使用:then = (Long) System.currentTimeMillis();并且当发现差异时,请确保在使用 System.currentTimeMillis() 而不是 getSystemTime() 时再次转换为 long
    【解决方案2】:

    我在Rohan 的帮助下找到了解决方案:)
    我根据我的要求调整了他的答案。

    当用户按下按钮时,就会启动一个线程。线程休眠到我想要的延迟,然后当它醒来时,它会执行我需要它执行的任何代码。当用户放手时,线程被杀死。这完成了我想要的,因为如果用户在线程唤醒之前放手,则线程被中断并且操作不会发生。

    我喜欢这种方法,因为它可以让我在延迟结束后立即执行我的业务逻辑,这很好,因为我可以给用户一些反馈,让他们知道他们已经推送了足够长的时间(手机可以振动,因为示例)。
    这种方法的缺点是:当您想要的操作正在运行时,用户可能会松开按钮,并在一切完成之前终止线程。这对我来说不是什么大问题,因为我的业务逻辑做的很少;它只是触发一个事件以供其他类处理。如果操作没有完全完成,用户可以重新尝试。

    代码比我想要的要长一些,但如果这是您的应用程序中的一个常见功能,那么它很容易重复使用。这是一个代码示例:

    protected class MyLongClickListener implements View.OnTouchListener {
        private Thread longClickSensor;
    
        public boolean onTouch(View view, MotionEvent event) {
            // If the user is pressing down and there is no thread, make one and start it
            if (event.getAction() == MotionEvent.ACTION_DOWN && longClickSensor == null) {
                longClickSensor = new Thread(new MyDelayedAction());
                longClickSensor.start();
            }
            // If the user has let go and there was a thread, stop it and forget about the thread
            if (event.getAction() == MotionEvent.ACTION_UP && longClickSensor != null) {
                longClickSensor.interrupt();
                longClickSensor = null;
            }
            return false;
        }
    
        private class MyDelayedAction implements Runnable {
            private final long delayMs = 1200;
    
            public void run() {
                try {
                    Thread.sleep(delayMs); // Sleep for a while
                    doBusinessLogic();     // If the thread is still around after the sleep, do the work
                } catch (InterruptedException e) { return; }
            }
            private void doBusinessLogic() {
                // Make sure this logic is as quick as possible, or delegate it to some other class
                // through Broadcasted Intents, because if the user lets go while the work is happenening,
                // the thread will be interrupted.
            }
        }
    }
    

    【讨论】:

    • 当用户使用第二根手指时,这将变得混乱。您应该检查是否已经启动了一个线程,或者您应该跟踪不同的选择器 ID。
    【解决方案3】:
    final boolean[] isLongPress = {false};
    final int duration = 3000;
    final Handler someHandler = new Handler();
        final Runnable someCall = new Runnable() {
            @Override
            public void run() {
                if(isLongPress[0]) {
                    // your code goes here 
                }
            }
        };
    
        someButton.setOnTouchListener(new View.OnTouchListener() {
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int eventAction = event.getAction();
                if(eventAction == MotionEvent.ACTION_DOWN){
                    isLongPress[0] = true;
                    someHandler.postDelayed(someCall, duration);
                }
                else if (eventAction == MotionEvent.ACTION_UP) {
                    isLongPress[0] = false;
                    someHandler.removeCallbacks(someCall);
                }
                return false;
            }
        });
    

    【讨论】:

    • MotionEvent.ACTION_MOVE 被触发并导致removeCallbacks
    【解决方案4】:

    这是我发现实现此行为的最简单方法。与当前接受的答案相比,它有几个优点。

    1. 通过检查view.isPressed,我们确保在触摸事件离开视图时不会触发onClickonLongClick。这模仿了系统的默认 onClickonLongClick 行为。
    2. 该事件已经存储了计时信息,因此无需在ACTION_DOWN 上存储开始时间或在ACTION_UP 上自行计算当前时间。这意味着我们可以同时将它用于多个视图,因为我们不会在 onTouch 之外的单个变量中跟踪事件开始时间。

    注意:ViewConfiguration.getLongPressTimeout() 是默认值,您可以更改该检查以使用您想要的任何值。

    注意:如果视图不能正常点击,您需要调用 view.setClickable(true) 以使 view.isPressed() 检查生效。

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        if (view.isPressed() && event.getAction() == MotionEvent.ACTION_UP) {
            long eventDuration = event.getEventTime() - event.getDownTime();
            if (eventDuration > ViewConfiguration.getLongPressTimeout()) {
                onLongClick(view);
            } else {
                onClick(view);
            }
        }
        return false;
    }
    

    如果您像@ampersandre 一样,希望在达到延迟时间后立即触发长点击事件,而不是等待ACTION_UP,那么以下对我来说很有效。

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            view.setTag(true);
        } else if (view.isPressed() && (boolean) view.getTag()) {
            long eventDuration = event.getEventTime() - event.getDownTime();
            if (eventDuration > ViewConfiguration.getLongPressTimeout()) {
                view.setTag(false);
                onLongClick(view);
            } else if (event.getAction() == MotionEvent.ACTION_UP) {
                onClick(view);
            }
        }
        return false;
    }
    

    【讨论】:

    • 嗨!谢谢你的回答,对我真的很有帮助。我对您的解决方案有一个小问题(一旦达到延迟时间就会立即触发长点击事件):它需要连续有触摸事件来检查持续时间是否 > 超时。因此,如果在按下按钮期间 U 没有小动作(移动事件...),onLongClick 触发可能会在超时之后发生。你有什么建议吗?非常感谢!
    【解决方案5】:

    我找到了一个简单的解决方案来研究长按事件的工作原理。 每次单击视图时,CheckForLongPress 类型的 Runnable 都会延迟添加到队列中。如果延迟结束,则调用OnLongClickListener。如果在延迟结束之前有不同的事件,那么,CheckForLongPress Runnable 将从队列中移除。

    我只是重写了视图的公共方法postDelayed(Runnable action, long delayMillis)来改变操作系统延迟

    @Override public boolean postDelayed(Runnable action, long delayMillis) {
        boolean isLongPress = action.getClass().getSimpleName().equals("CheckForLongPress");
        return super.postDelayed(action, isLongPress ? LONG_PRESS_MILLIS : delayMillis);
    }
    

    我将LONG_PRESS_MILLIS 设置为 100,它正在工作!

    希望对你有帮助!!! ;)

    【讨论】:

      【解决方案6】:

      一种方法可以做到。使用简单,容易配置时间长度,准时触发回调。

      public static void setOnLongClickListener(final View view, final View.OnLongClickListener longClickListener, final long delayMillis)
      {
          view.setOnTouchListener(new View.OnTouchListener()
          {
              final Handler handler = new Handler();
              final Runnable runnable = new Runnable()
              {
                  @Override
                  public void run()
                  {
                      longClickListener.onLongClick(view);
                      mRunning = false;
                  }
              };
      
              boolean mRunning;
              boolean mOutside;
              RectF mRect = new RectF();
      
              @Override
              public boolean onTouch(View v, MotionEvent event)
              {
                  switch (event.getAction())
                  {
                      case MotionEvent.ACTION_DOWN:
                      {
                          handler.postDelayed(runnable, delayMillis);
                          mRunning = true;
                          mOutside = false;
                          mRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                          break;
                      }
                      case MotionEvent.ACTION_MOVE:
                          if (!mOutside)
                          {
                              mOutside = !mRect.contains(v.getLeft() + event.getX(), v.getTop() + event.getY());
                              if (mOutside)
                              {
                                  handler.removeCallbacks(runnable);
                                  mRunning = false;
                              }
                          }
                          break;
                      case MotionEvent.ACTION_UP:
                      {
                          if (mRunning)
                              v.performClick();
                          handler.removeCallbacks(runnable);
                          mRunning = false;
                          break;
                      }
                      case MotionEvent.ACTION_CANCEL:
                      {
                          handler.removeCallbacks(runnable);
                          mRunning = false;
                          break;
                      }
                  }
                  return true; // !!!
              }
          });
      }
      

      【讨论】:

        【解决方案7】:

        我找到了这个方法。就是这么简单。

        private static final long HOLD_PRESS_TIME = 1200; //ms unit    
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_control);
        
            //Declare your button and add listener
            ImageView iconImg = findViewById(R.id.more_icon);
            iconImg.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    switch (motionEvent.getAction()){
                        case MotionEvent.ACTION_DOWN:
                            Log.w("Button", "Button is pressed...");
                            //Start timer
                            timer.start();
                            break;
                        case MotionEvent.ACTION_UP:
                            Log.w("Button", "Button is released...");
                            //Clear timer
                            timer.cancel();
                            break;
                    }
                    return false;
                }
            });
        
        
        }
        
        private CountDownTimer timer = new CountDownTimer(HOLD_PRESS_TIME, 200) {
            @Override
            public void onTick(long l) {
                Log.w("Button", "Count down..."+l); //It call onFinish() when l = 0
            }
        
            @Override
            public void onFinish() {
                //TODO your action here!
                Log.w("Button", "Count finish...");
        
            }
        };
        

        【讨论】:

          【解决方案8】:

          为了将来参考,这是我的实现:)

          public abstract class MyLongClickListener implements View.OnTouchListener {
          
              private int delay;
              private boolean down;
              private Runnable callback;
              private RectF mRect = new RectF();
          
              public MyLongClickListener(int delay) {
                  this.delay = delay;
                  this.callback = new Runnable() {
                      @Override
                      public void run() {
                          down = false;
                          onLongClick();
                      }
                  };
              }
          
              public abstract void onLongClick();
          
              @Override
              public boolean onTouch(View v, MotionEvent e) {
                  if (e.getPointerCount() > 1) return true;
          
                  int action = e.getAction();
                  if (action == MotionEvent.ACTION_DOWN) {
                      down = true;
                      mRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                      v.postDelayed(callback, delay);
                      return false;
          
                  } else if (action == MotionEvent.ACTION_MOVE) {
                      if (down && !mRect.contains(v.getLeft() + e.getX(), v.getTop() + e.getY())) {
                          v.removeCallbacks(callback);
                          down = false;
                          return false;
                      }
          
                  } else if (action == MotionEvent.ACTION_UP) {
                      if (down) {
                          v.removeCallbacks(callback);
                          v.performClick();
                      }
                      return true;
          
                  } else if (action == MotionEvent.ACTION_CANCEL) {
                      v.removeCallbacks(callback);
                      down = false;
                  }
          
                  return false;
              }
          
          }
          

          用法:

          textView.setOnTouchListener(new MyLongClickListener(2000) {
              @Override
              public void onLongClick() {
                  System.err.println("onLongClick");
              }
          });
          

          【讨论】:

            猜你喜欢
            • 2016-11-08
            • 1970-01-01
            • 1970-01-01
            • 2018-08-09
            • 2010-10-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-27
            相关资源
            最近更新 更多