Android OnTouchListener长按增量异常:短按触发增量且抬起未停止
问题排查与修复方案
核心问题分析
- 短按后仍触发增量:
ACTION_UP仅取消了CountDownTimer,但未取消postDelayed的延迟任务,导致手指抬起后,750ms延迟结束仍会启动计时器。 - CountDownTimer无法复用:
CountDownTimer调用cancel()后无法再次调用start(),原代码复用同一个实例会导致后续长按无法触发增量。 - 触摸事件未消费:
onTouch返回false,会让触摸事件继续向下传递,可能导致ACTION_UP事件未被当前监听器正确捕获。
修复后的代码
launchableSelectionView.setOnTouchListener(new View.OnTouchListener() { // 保存延迟任务引用,用于取消 private Runnable longPressRunnable; // 保存计时器实例,用于取消 private CountDownTimer countDownTimer; @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: // 每次按下创建新的延迟任务 longPressRunnable = new Runnable() { @Override public void run() { // 每次启动计时器都新建实例,避免复用已cancel的对象 countDownTimer = new CountDownTimer(Long.MAX_VALUE, 100) { @Override public void onTick(long l) { MissileSystem system = bFittedToPlayer ? game.GetOurPlayer().GetInterceptorSystem() : game.GetSAMSite(lFittedToStructureID).GetInterceptorSystem(); int lFreeSlots = system.GetEmptySlotCount() - Types.size(); int lAvailableFunds = game.GetOurPlayer().GetWealth() - lTotalCost; if (lFreeSlots == 0) { activity.ShowBasicOKDialog(context.getString(R.string.insufficient_slots)); this.cancel(); // 无插槽时停止计时器 } else if (lAvailableFunds < game.GetConfig().GetInterceptorCost(type)) { activity.ShowBasicOKDialog(context.getString(R.string.insufficient_funds)); this.cancel(); // 资金不足时停止计时器 } else { launchableSelectionView.IncrementNumber(); AddType(type.GetID()); } } @Override public void onFinish() {} }; countDownTimer.start(); } }; view.postDelayed(longPressRunnable, 750); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // 取消延迟任务和计时器 if (longPressRunnable != null) { view.removeCallbacks(longPressRunnable); } if (countDownTimer != null) { countDownTimer.cancel(); } break; } // 返回true,确保触摸事件被当前监听器消费,避免事件传递丢失 return true; } });
关键修复点
- 取消延迟任务:在
ACTION_UP和ACTION_CANCEL时调用removeCallbacks,取消未执行的延迟任务,避免短按后触发增量。 - 新建CountDownTimer实例:每次长按触发时创建新的计时器,解决复用已cancel实例无法启动的问题。
- 消费触摸事件:
onTouch返回true,确保ACTION_UP等事件能被当前监听器正确捕获,防止事件传递导致的异常。 - 边界情况处理:在无插槽或资金不足时主动取消计时器,避免无效循环。
内容的提问来源于stack exchange,提问作者user19113197
相关产品推荐
相关产品推荐

