MotionEvent.ACTION_UP未被调用

请考虑休耕(为了更好地理解我的问题)。 在这里输入图像说明

正如你所看到的,我正在考虑一个由填充包围的列表视图。 现在,如果用户按下列表视图项目,作为我已经提供了浅蓝色背景颜色的行动。 现在,我的应用程序正在处理onTouch事件本身来确定类似的操作

  • 点击
  • 从左到右滑动
  • 从右到左滑动

这是我的代码。

public boolean onTouch(View v, MotionEvent event) { if(v == null) { mSwipeDetected = Action.None; return false; } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { downX = event.getRawX(); downY = event.getRawY(); mSwipeDetected = Action.Start; // Find the child view that was touched (perform a hit test) Rect rect = new Rect(); int childCount = listView.getChildCount(); int[] listViewCoords = new int[2]; listView.getLocationOnScreen(listViewCoords); int x = (int) event.getRawX() - listViewCoords[0]; int y = (int) event.getRawY() - listViewCoords[1]; View child; for (int i = 0; i < childCount; i++) { child = listView.getChildAt(i); child.getHitRect(rect); if (rect.contains(x, y)) { mDownView = child; break; } } return false; // allow other events like Click to be processed } case MotionEvent.ACTION_MOVE: { upX = event.getRawX(); upY = event.getRawY(); float deltaX=0,deltaY=0; deltaX = downX - upX; deltaY = downY - upY; if(deltaY < VERTICAL_MIN_DISTANCE) { setTranslationX(mDownView, -(deltaX)); setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / listView.getWidth()))); return false; } else { forceBringBack(v); } return false; } case MotionEvent.ACTION_UP: { stopX = event.getX(); float stopValueY = event.getRawY() - downY; float stopValue = stopX - downX; if(!mDownView.isPressed()) { forceBringBack(mDownView); return false; } boolean dismiss = false; boolean dismissRight = false; if(Math.abs(stopValue)<10) { mSwipeDetected = Action.Start; } else { mSwipeDetected = Action.None; } String log = ""; Log.d(log, "Here is Y" + Math.abs(stopValueY)); Log.d(log, "First Comparison of Stop Value > with/4" + (Math.abs(stopValue) > (listView.getWidth() /4))); Log.d(log, "Second Comparison " + (Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE)); Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value " + stopValue); if((Math.abs(stopValue) > (listView.getWidth() /4))&&(Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE)) { dismiss = true; dismissRight = stopValue > 0; if(stopValue>0) { mSwipeDetected = Action.LR; } else mSwipeDetected = Action.RL; } Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value after dissmiss" + stopValue); if(dismiss) { if(dismissRight) mSwipeDetected = Action.LR; else mSwipeDetected = Action.RL; animate(mDownView) .translationX(dismissRight ? listView.getWidth() : - listView.getWidth()) .alpha(0) .setDuration(mAnimationTime) .setListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { } }); } else { animate(mDownView) .translationX(0) .alpha(1) .setDuration(mAnimationTime) .setListener(null); } break; } } return false; } 

正如你所看到的,我确定了MotionEvent.ACTION_UP中执行的动作,并相应地设置了Enum Action的值。 如果用户不跨越列表视图边界,这个逻辑就像一个魅力。

现在,如果用户在滑动(或具体地)沿着列表项移动他的手指从蓝色移动到橙色时,MotionEvent.ACTION_UP将不会被提供给列表视图,这导致我的代码不作出决定,并且由于翻译X ()方法和setAlpha(),因为在这种情况下没有确定Action,那个特定的列表项变为空白。

这个问题在这里并没有停止,因为我每次都没有查看充气的视图,同一个translationX()行每次膨胀导致多次出现空白/白色列表项。

有没有什么可以做,即使我没有遇到MotionEvent.ACTION_UP,我仍然可以做出决定?

谢谢。

你应该return true; case MotionEvent.ACTION_DOWN: ,那么MotionEvent.ACTION_UP将被处理。


正如View.OnTouchListener所解释的:

返回

如果侦听器消耗了该事件,则返回true,否则返回false。

ACTION_UP在发生ACTION_UP之前不会被调用,对此的逻辑解释是,如果在其之前从未发生ACTION_DOWN ,则不可能发生ACTION_UP

这个逻辑使得开发者能够在ACTION_DOWN之后阻塞进一步的事件。

另外请注意,在某些情况下(例如,屏幕旋转),手势可能被取消,在这种情况下, MotionEvent.ACTION_UP将不会被发送。 而是发送一个MotionEvent.ACTION_CANCEL 。 因此,一个正常的操作switch语句应该是这样的:

 switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: // check if we want to handle touch events, return true // else don't handle further touch events, return false case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // finish handling touch events // note that these methods won't be called if 'false' was returned // from any previous events related to the gesture } 

我不认为增加return true; 以案件MotionEvent.ACTION_DOWN:将最终解决问题。 这只会让return false的工作像魅力那样复杂化。

需要注意的是: MotionEvent.ACTION_DOWN: /*something*/ return true; 会阻止任何其他Listenercallback可用的视图,即使onClickListenerm,而在MotionEvent.ACTION_UP:正确return false MotionEvent.ACTION_UP:可以帮助MotionEvent传播到正确的目的地。

参考他原来的代码sourse: https : //github.com/romannurik/android-swipetodismiss

正如Danpe在简洁的答案中解释的那样 – 我必须添加ACTION_DOWN代码才能识别ACTION_UP。

  case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: XyPos xyPos = new XyPos(); xyPos.x = last_x; xyPos.y = last_y; handleViewElementPositionUpdate(xyPos); break; 

我有整个onTouch(..)方法返回true,所以我不知道为什么这是不够的,但很高兴有这个快速的解决scheme..(谢谢!)