androidgestureonfling

Sometimes GestureDetector is skipping onFling


I have an application which uses GestureDetector and I am replacing some images onDown and onFling (aka onUp). However in some cases onFling is not called and the outcome is not pretty :)

Have you faced such issue and have you found a fix/workaround(besides using a timeout)?
Here is a little code:

    final GestureDetector gdt1 = new GestureDetector(getApplicationContext(), new MyGestureDetector(mGestDetector, R.id.weatherFrame1));
    FrameLayout weatherFrame1 = (FrameLayout)findViewById(R.id.weatherFrame1);
    if (weatherFrame1 != null)
    {
        weatherFrame1.setOnTouchListener(new View.OnTouchListener()
        {
            @Override
            public boolean onTouch(final View view, final MotionEvent event)
            {
                gdt1.onTouchEvent(event);
                return true;
            }
        });
    }

And here is part of MyGestureDetector.java

    public class MyGestureDetector implements GestureDetector.OnGestureListener{
    {
    ...
        @Override
        public boolean onDown(MotionEvent e)
        {
            int index = e.getActionIndex();
            int pointerId = e.getPointerId(index);

            if (startingPointerId == -1)
            {
                Log.i("MyGestureDetector", "Pointer is " + pointerId);

                if (pointerId == 0)
                {
                    startingPosX = e.getX(pointerId);
                    startingPosY = e.getY(pointerId);
                    startingPointerId = pointerId;
                    if (null != mGestureListener)
                    {
                        mGestureListener.onDown(mGestureOrigin);
                    }
                }
            }
            return true;
        }
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
        {
            startingPointerId = -1;
            if (null != mGestureListener)
            {
                mGestureListener.onUp(mGestureOrigin);
            }
            return true;
        }
    }

Solution

  • For detecting "up" and "down" events, I would recommend something simple like this:

    @Override
    public boolean onTouch(final View view, final MotionEvent event) {
    
       if (event.getAction() == MotionEvent.ACTION_UP) {
          // handle up event
       } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
          // handle down event
       }
    
    }
    

    Edit: The most likely reason onFling isn't being called every time is because it isn't simply a way to handle UP events. Looking at the Android source code, onFling is only called when the velocity reaches the minimum velocity required to be considered a "fling." Check it out:

     case MotionEvent.ACTION_UP:
            mStillDown = false;
            MotionEvent currentUpEvent = MotionEvent.obtain(ev);
            if (mIsDoubleTapping) {
                // Finally, give the up event of the double-tap
                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            } else if (mInLongPress) {
                mHandler.removeMessages(TAP);
                mInLongPress = false;
            } else if (mAlwaysInTapRegion) {
                handled = mListener.onSingleTapUp(ev);
            } else {
    
                // A fling must travel the minimum tap distance
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                final float velocityY = velocityTracker.getYVelocity();
                final float velocityX = velocityTracker.getXVelocity();
    
                if ((Math.abs(velocityY) > mMinimumFlingVelocity)
                        || (Math.abs(velocityX) > mMinimumFlingVelocity)){
                    handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
                }
            }
    

    Most notably:

     if ((Math.abs(velocityY) > mMinimumFlingVelocity)
              || (Math.abs(velocityX) > mMinimumFlingVelocity)){
          handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
     }
    

    (Source: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/view/GestureDetector.java)