Gesture Listener onFling Not Acting Consistant

断了今生、忘了曾经 提交于 2019-12-08 16:38:15

问题


Update: See bounty for expanded question.

I have a GestureDetector setup on a ListView. The ListView is an entire fragment that comes from side of window and overlays another fragment partially. I want to give user ability to swipe it close (i.e. Wunderlist is a great example of this function on right side).

Here is my setup:

    gestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            if (gestureDetector.onTouchEvent(event)) {
                return true;
            }

            return false;
        }
    };
    listView.setOnTouchListener(gestureListener); 

The Listener itself:

public class GestureListener extends SimpleOnGestureListener {

        private static final int SWIPE_MIN_DISTANCE = 180;
        private static final int SWIPE_THRESHOLD_VELOCITY = 50;

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {

            try {
                if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                        && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {

                    FragmentManager fma = getActivity()
                            .getSupportFragmentManager();



                    FragmentManager fm = getFragmentManager();
                    FragmentTransaction t = fm.beginTransaction();

                    SherlockListFragment mFrag = new RateReviewFragment();

                    t.add(R.id.main_frag, mFrag);
                    t.setCustomAnimations(R.anim.animation_enter,
                            R.anim.animation_leave);
                    t.remove(mFrag);
                    t.commit();

                }
                return false;
            } catch (Exception e) {

            }
            return false;
        }





        @Override
        public void onLongPress(MotionEvent e) {

            // edited out; this opens up a context menu
        }

    }

Sometimes, when the ListView scrolls to the bottom (or list scrolling is interrupted with clicks on a list row) the GestureListener simply stops ... listening. Swiping will not work. You have to scroll back to top to get it to work again.

If anyone can help me isolate these issues I would be grateful!

UPDATE : Only one issue remains, "the listener stops listening"

UPDATE TWO: I may have figured out what is CAUSING this; just dont know the fix:

I have found something out after logging my actions; The lower I get in the list (and the more I interact with it), the measurements are not consistent. For example, if I move my finger a centimeter left and right - at very top of list, it will say I moved, for example, 200 pixels. But when I have the problem stated above, it is far lower, maybe 50, for the SAME 1 centimeter distance. So the window doesn't close because it's not meeting my if conditions.

Sidenote: I have stated more than once, the problem is when "interacting with the list". This means: If I quickly scroll from top to bottom; no issue; but If I slowly work my way down, perhaps tapping on the screen, scrolling on and off, clicking buttons on the listView, some of which open a new activity (and then come back to same position), this is "interacting" with".


回答1:


try this,

    gestureDetector = new GestureDetector(getActivity(),
            new GestureListener());
    View.OnTouchListener gestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
           listView.onTouchEvent(event);
           gestureDetector.onTouchEvent(event);

            return true;
        }
    };
    listView.setOnTouchListener(gestureListener);

and make the following change

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
  return true;
}

EDIT: I saw the app wunderlist, if u want to simulate that functionality try out this approach,

Use onInterceptTouch for the root layout (of which listView is a child of), inside this method u have to check whteher the user has swiped or not like the following,

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_MOVE) {
    if(/* Check your condition here */) { 
      // User has swiped 
      return true;
    }
  }

  return false; // return false so that the event will be passed to child views.
}

for more info Android onInterceptTouchEvent




回答2:


change the onTouch this way:

@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
     boolean consumed = gestureDetector.onTouchEvent(event);
     if (event.getAction() == MotionEvent.ACTION_MOVE) {
                return false;
     }

    return consumed;
}



回答3:


Don't really sure if this will help as god knows what's going on behind the scene with all this fragments issues but try this:

After onTouch has been called , before returning false check if gestureDetector is still alive (not null),(By Sending callback or something to the listview class) if not create new instance of it and call again to

listView.setOnTouchListener(gestureListener); 



回答4:


First

I note that in your gesture listener you return true in onDown. If I've understood your question correctly you are leaving tap and vertical swipe events for the ListView, and so you are only interested in horizontal swipe events in your code.

Why not remove all the functions you are not interested in, thus leaving everything you aren't interested in to the ListView? If you have to have them, at least return false, which is the default (see here).

As an aside, the code for AbsListView (the parent of ListView) shows a pretty complex state system for handling taps and swipes, so it's probably best left as untocuhed as possible in my view

Secondly

Given your more recent observations, perhaps the key is to consider the ratio between X and Y displacement rather than absolute size of swipe. Whilst I can't explain the change in reported size of swipe, what we are really interested in is a movement which is predominantly horizontal, ie:

(change in X) >> (change in Y)

This can be achieved as follows:

if ((velocityY == 0.0f) || (Math.abs(velocityX/velocityY) > 3.0f) {
    // The movement is predominantly horizontal
    // Put other checks here (like direction and so on)
    // Then do the stuff
}

This has the advantage of not using e1 or e2, which brings us on to ...

Thirdly

As you note, the issue is when you have interacted with the listview. At this point, there may be multiple pointers being reported in the Event. It is not guaranteed that the default X and Y information returned by them relates to any particular pointer event. It is probably worth checking that you are comparing information from the same pointer event if you do want to do calculations involving X and Y, using something like:

int numPointers = e1.getPointerCount();
for (int pointerIndex1 = 0; pointerIndex1 < numPointers; pointerIndex1++) {
    int pointerId = e1.getPointerId(pointerIndex1);
    // find index for pointerId in e2.

    float deltaX = e2.getX(pointerIndex2) - e1.getX(pointerIndex1);
    // and so on
}



回答5:


Firtly try to make OnTouchListener like this:

View.OnTouchListener gestureListener = new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
    gestureDetector.onTouchEvent(event);
        return true;
    }
};

Then - you're always returning false in onFling(). Return true in try/catch clause. And try to pass the same data to the onScroll method in else clause inside onFling.



来源:https://stackoverflow.com/questions/16971808/gesture-listener-onfling-not-acting-consistant

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!