I'm developing an app which have a ViewPager
as a menu. Each fragment
has a bunch of child views which can be ListView
, ScrollView
, LinearLayout
, etc...
One of this fragments
have a settings button which toggles a settings panel (LinearLayout
wrapper) with a ScrollView
and some LinearLayout
(buttons) and SeekBar
as childs. This settings panel is animated with a slide up or down animation (when dismissed) and when it's visible I disable the ViewPager
paging:
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!pagingEnabled && event.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (!pagingEnabled && event.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
return super.onInterceptTouchEvent(event);
}
public boolean isPagingEnabled() {
return pagingEnabled;
}
public void setPagingEnabled(boolean pagingEnabled) {
this.pagingEnabled = pagingEnabled;
}
But this came with a problem, every time the panel is up all it child views wouldn't receive the OnTouchEvent
and that's why I've added a GestureDetector.SimpleOnGestureListener
:
protected class YScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(C.TAG, "distanceX " + distanceX + " distanceY " + distanceY);
return Math.abs(distanceY) < Math.abs(distanceX);
}
}
and changed my ViewPager
onInterceptTouchEvent
to:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (!pagingEnabled && event.getAction() == MotionEvent.ACTION_MOVE && (mGestureDetector != null && mGestureDetector.onTouchEvent(event))) {
return true;
}
return super.onInterceptTouchEvent(event);
}
This works, the panel buttons receive their onClick, the ListView swipes, etc... but this doesn't work so perfect because Math.abs(distanceY) < Math.abs(distanceX)
it's not that accurate. If I fast swipe up and down or diagonally or if I touch a button with a minor swipe the onInterceptTouchEvent
will return true
because mGestureDetector.onTouchEvent(event)
will return true
too.
After some google search I came across this:
viewPager.requestDisallowInterceptTouchEvent(true);
And I tried something like this:
myListView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
viewPager.requestDisallowInterceptTouchEvent(true);
return false;
}
});
And it works really well because ViewPager.onInterceptTouchEvent
it's called first with MotionEvent.ACTION_DOWN
and then myListView.setOnTouchListener
it's called right after and disallows the remaining MotionEvent
actions (MOVE and UP) and I can fast swipe up, down, sides ways, etc. The ViewPager
wont page but the ListView
swipes like a charm.
But a problem still remains, I've to add this requestDisallowInterceptTouchEvent(true)
to all child views onTouchEvent
, and it's not elegant code.
So my question is, I am on the right path? Is there anyway to avoid adding this listener to all the panel child views (of course if I have to I'll do it in the most generic way)?
Thanks for your time.
Disabling paging should be as simple as returning false
from both onInterceptTouchEvent
and onTouchEvent
. You shouldn't need extra gesture detection to get around the ViewPager
. Extend ViewPager
like this:
public class MyViewPager extends ViewPager {
private boolean pagingEnabled = true;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPagingEnabled(boolean pagingEnabled) {
this.pagingEnabled = pagingEnabled;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return pagingEnabled && super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return pagingEnabled && super.onTouchEvent(event);
}
}
I use the same thing (with a different name) for the same reason in one of my apps and it definitely works.