Reputation: 1225
In my Android-App I've the following layout:
<CustomRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.view.ViewPager
android:id="@+id/image_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<OverScrollBounceScrollView
android:id="@+id/scrollview"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
>
<LinearLayout
android:id="@+id/user_profile_scroll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<RelativeLayout
android:id="@+id/dummy_viewport"
android:layout_width="match_parent"
android:layout_height="350dp"
>
</RelativeLayout>
<LinearLayout
android:id="@+id/scroll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/white"
>
...some content
</LinearLayout>
</LinearLayout>
</OverScrollBounceScrollView>
</CustomRelativeLayout>
It looks about this:
https://i.sstatic.net/lzL5u.png
My goals are:
The problem now is, that the scrollview takes all the events and the viewpager can't be reached at all.
Any ideas on how to solve this?
Upvotes: 3
Views: 2399
Reputation: 1225
For anyone who is interested in an answer to this question or has similar issues, here is how I solved it.
First of all, the general approach is to make a custom view that intercepts certain touch events and delegates them to the correct childs.
In my case a viewpager should intercept the horizontal gestures and the scrollview that is laying on top of the viewpager should only get the vertical scroll-events. One special requirement for me was, that the viewpager should also intercept only touch-events within a certain bound, namely a dummy-viewport, which is a child inside the overlaying scrollview.
Within the custom layout class one has to override the onInterceptTouchEvent() method in conjunction with the onTouchEvent() method:
@Override
public boolean onInterceptTouchEvent ( MotionEvent event ) {
// 1.) remember DOWN event ALWAYS as this is important start for every gesture
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
mInitialDownEvent = MotionEvent.obtainNoHistory(event);
mIsDownEventDispatched = false;
break;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float y = event.getY();
final int yDiff = (int) Math.abs(y - mInitialY);
final int xDiff = (int) Math.abs(x - mInitialX);
calculateDelegatesHitRectangle();
if(xDiff > mTouchSlop && mDelegateBounds.contains((int)event.getX(), (int)event.getY())){
// 2.) if we scroll more in X-direction AND we are within the bounds of our delegatinView
// then INTERCEPT event here, so this views onTouch will be called
Log.d("hitrect", "HITRECT--- TOP: " + mDelegateBounds.top + " BOTTOM: " + mDelegateBounds.bottom);
return true;
}
break;
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 3.) we receive ALL following event after X-Scroll has been intercepted
// but the DOWN event was handled by this view so we have to dispatch the remembered down event
// so the dispatch-target has a valid start for the scroll-gesture
if(!mIsDownEventDispatched){
mDispatchTargetView.dispatchTouchEvent(mInitialDownEvent);
mIsDownEventDispatched = true;
}
mDispatchTargetView.dispatchTouchEvent(event);
Log.d("touch", "ROOT: onTouchEvent after Intercept " + event.getActionMasked());
return true;
}
As you can see, I always check and save the DOWN-event in a class member and as soon as a movement is detected, I check if it is performed in x-direction (for the viewpager). Is intercepted in then. My calculateDelegatesHitRectangle() method in this case only checks if I'am within the bounds, where I wanna except the touch events.
In onTouch I only reroute the intercepted horizontal gesture to my viewpager, which is injected to this custom view via a setter. In order to have a correct gesture, I also need to reroute the intercepted DOWN event.
From my Activity code I just set the dispatch target and the delegate view:
dispatchAwareLayout.setDispatchTargetView(mImageViewPager);
dispatchAwareLayout.setDelegateView(mDummyViewport);
Upvotes: 5