I think your idea to have the ScrollView dictate when the ViewPager should kick in is pretty good, it's already using requestDisallowInterceptTouchEvent
to prevent the ViewPager from intercepting the scroll. What you need is use this same method to allow the intercept in the circumstances you've indicated, which is when the ScrollView has reached the top or bottom.
So if the ScrollView handles an ACTION_MOVE
touch event but the resulting scroll offset hasn't changed we can assume that we've scrolled past the top or bottom (and are now overscrolling). There may be a better way to detect overscroll so have an experiment with this as a starting point:
//MyScrollView.java
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = super.onTouchEvent(ev);
if (handled && ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
int y = getScrollY();
boolean moved = y != prevY;
prevY = y;
// This is the important part to allow the parent to intercept
if (!moved) {
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
}
// Also important to re
return moved;
}
return handled;
}
EDIT - I had a quick play around and think this works nicely
public class MyScrollView extends ScrollView {
private boolean clampedY;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = super.onTouchEvent(ev);
if (handled) {
ViewParent parent = getParent();
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
if (clampedY && parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_DOWN:
clampedY = false;
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
break;
}
}
return handled;
}
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
this.clampedY = clampedY;
}
}
EDIT 2 - This will keep ownership of the touch if there has been any scroll, so a separate touch is required to switch page
public class MyScrollView extends ScrollView {
private boolean scrolled;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = super.onTouchEvent(ev);
if (handled) {
ViewParent parent = getParent();
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
if (!scrolled && parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_DOWN:
scrolled = false;
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
break;
}
}
return handled;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (t != oldt) {
scrolled = true;
}
}
}