问题
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