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!