Reputation: 1373
I have a RelativeLayout
that contains many child views with various touch events. I want to get notified when the user swipes anywhere on the parent RelativeLayout
so I can update some UI while still letting the child views handle their own touch/drag events. What is the standard way of accomplishing this for Android?
I was thinking that I could put an overlay over all the views and have it detect swipe gestures and if it wasn't a swipe I could pass the touch event on to other views in the hierarchy. It doesn't seem like Android supports that sort of touch detection and once one view decides to see if a event is a certain gesture no other views will be able to see the events.
A swipe gesture consists of three touch events: ACTION_DOWN, ACTION_MOVE and ACTION_UP. You need to record all three events and then see if it was a swipe or not. If it was not a swipe then we would need to pass those events to other child views to see if it meets their criteria for the gesture they are looking for. If it is a swipe we would want to block the events from being sent to the child view. Just not sure if this is actually possible.
Update
Using the ideas of the users in the answers section I was able to write a layout that met my specification. This RelativeLayout
just handles right and left swipes but could be added to to handle more directions. OnSwipeListener
is just an interface
with two methods void swipedLeft()
and void swipedRight()
.
public class SwipeRelativeLayout extends RelativeLayout {
public OnSwipeListener mSwipeListener = null;
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private float mStartX = 0;
private float mStartY = 0;
private float mEndX = 0;
private float mEndY = 0;
public SwipeRelativeLayout(Context context) {
super(context);
}
public SwipeRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SwipeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean handled = onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) return handled;
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mStartX = event.getRawX();
mStartY = event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
float distanceX = event.getRawX() - mStartX;
float distanceY = event.getRawY() - mStartY;
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD) {
if (distanceX > 0) {
if (mSwipeListener != null) mSwipeListener.swipedRight();
} else {
if (mSwipeListener != null) mSwipeListener.swipedLeft();
}
return true;
}
return false;
}
}
return true;
}
}
Upvotes: 0
Views: 1154
Reputation: 2239
When a touch event occurs it is first passed to the parent layout view and passed on to child view via onInterceptTouchEvent
returning true or false. You want to override and intercept the touch events on the parent RelativeLayout
and determine if you have seen a swipe gesture or not. If you have seen a swipe you want to return that you have handled it. In this case ACTION_UP
is the end of a possible swipe and if your onTouchEvent
handled the event then you can return true and the views below it will not get the finishing event and thus ignore their gestures.
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean handled = onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) return handled;
return false;
}
Upvotes: 3