2

I am making a menu where the items have to respond to two types of events:

OnClick
If the user clicks the item, it should start an Activity. Each button launches a different activity.

OnFling
When the user swipes the item from right to left, its name should appear. Each item has a different name, and the name will show up just in the item that was swiped.

I tried a lot of different things, searched for many tutorials and questions here on StackOverflow but I couldn't find a decent answer and my implementation stills poor.

What I am doing right now is setting an OnClick handle for each item in the method getView of my custom adapter and setting a gestureListener on my listView and handling the method onFling, but it is not acting as expected: most of the time I try to swipe and the gesture is handled as a click...

I know I could use an implementation like this, but I think I can not use the onTouch event trick in this case, because I need the view position so I can handle which activity should be launched (onTouch/click) or which TextView should become visible.

Here are the snippets that I have so far:

    final GestureDetector gestureDetector = new GestureDetector(
            new GestureListener());

    lvMenu.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(final View view, final MotionEvent event) {
            if (gestureDetector.onTouchEvent(event)) {
                return false;
            }

            return true;
        }
    });

The inner class:

class GestureListener extends SimpleOnGestureListener {
    protected MotionEvent mLastOnDownEvent = null;

    @Override
    public boolean onDown(final MotionEvent e) {
        return true;
    }

    @Override
    public boolean onFling(final MotionEvent e1, final MotionEvent e2,
            final float velocityX, final float velocityY) {
        Log.d("OnFling", "Called");
        final int SWIPE_MIN_DISTANCE = 20;
        final int SWIPE_MAX_OFF_PATH = 250;
        final int SWIPE_THRESHOLD_VELOCITY = 200;
        try {
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
                return false;
            }
            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                Log.i("A", "Right to Left");
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                Log.i("B", "Left to Right");
            }
        } catch (final Exception e) {
            // nothing
        }
        return super.onFling(e1, e2, velocityX, velocityY);
    }
}

And the onClick() method inside my custom adapter:

public void onClick(final View v) {
                Intent i = null;
                switch (position) {
                case 0:
                    i = new Intent(parent.getContext(),
                            ProfileActivity.class);
                    break;
                case 1:
                    i = new Intent(parent.getContext(),
                            CatalogActivity.class);
                    break;
                case 2:
                    i = new Intent(parent.getContext(), GuideActivity.class);
                    break;
                case 3:
                    i = new Intent(parent.getContext(),
                            EventsActivity.class);
                    break;
                default:
                    Log.d("MenuActivity", "Switch default case.");
                    break;
                }
                if (i != null) {
                    parent.getContext().startActivity(i);
                }

            }

Thank you :)

EDIT 1: now I am trying to set a OnTouchListener to each View in my custom adapter. But it is not working... I can't figure out the reason why.

This is how I am setting up the TouchListener:

            convertView.setOnTouchListener(new MenuGestureHandler() {

            @Override
            public boolean onSwipeRight() {
                Log.d("Swipe Right", "OK!");
                return true;
            }

            @Override
            public void onClick() {
                Log.d("Click", "OK!");
            }

        });

And this is the class (with some useless code like swipeUp, swipeDown):

public class MenuGestureHandler implements OnTouchListener {

GestureDetector gestureDetector = new GestureDetector(new GestureListener());

@Override
public boolean onTouch(final View v, final MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
}

private final class GestureListener extends SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onSingleTapConfirmed(final MotionEvent e) {
        onClick(); // my method
        return super.onSingleTapConfirmed(e);
    }

    @Override
    public boolean onFling(final MotionEvent e1, final MotionEvent e2,
            final float velocityX, final float velocityY) {
        boolean result = false;
        try {
            final float diffY = e2.getY() - e1.getY();
            final float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD
                        && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        result = onSwipeRight();
                    } else {
                        result = onSwipeLeft();
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD
                        && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        result = onSwipeBottom();
                    } else {
                        result = onSwipeTop();
                    }
                }
            }
        } catch (final Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

public boolean onSwipeRight() {
    return false;
}

public boolean onSwipeLeft() {
    return false;
}

public void onClick() {
}

public boolean onSwipeTop() {
    return false;
}

public boolean onSwipeBottom() {
    return false;
}

EDIT 2 I could get it to work by just overriding the method onDown() in my GestureListener class and setting it to always return true. I really don't understand why I need to do that, but it seems it is working now!

Community
  • 1
  • 1
Gabriel R.
  • 303
  • 2
  • 15

2 Answers2

4

There may be a few problems to consider here, but the first and foremost is the hierarchy of touch events.

From what I gather, the GestureDetector is attached to the ListView via an OnTouchListener, and the OnClickListener is set on the individual list items--which are child views of the ListView.

This is a problem because of touch handling order. ListView (and really any ViewGroup) will deliver touch events to its child views first, and handle the events directly if and only if no child views consume the event. A child view with an OnClickListener will always consume touch events because it is tracking them for a click event...so your existing swipe detector never (or rarely) sees any events.

The simplest solution would likely be to avoid splitting the touch handling between two elements. Use a single GestureDetector for each list item that handles both the click (i.e. single tap up) and the swipe--and make sure it's set on the items themselves...not the parent ListView.

You might also consider watching my talk on the Android Touch System to give you a more detailed idea of what you are dealing with.

devunwired
  • 62,780
  • 12
  • 127
  • 139
  • Alright, perfect. Now I understand the issue... But I cannot attach a OnTouchListener to each item, I don't know why! It just does not work... Can you help me with that? I edited my post with the newest code. Thank you so much. – Gabriel R. Feb 16 '15 at 05:56
0

I know this is an old question for here is the answer for future searches:

https://stackoverflow.com/a/44426980/6776960

Hardik Maru
  • 661
  • 5
  • 8