从Scrollview发送事件到Viewpager的底层

时间:2014-01-23 17:18:00

标签: android touch scrollview android-custom-view android-event

在我的Android-App中,我有以下布局:

<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>

它看起来像这样:

http://i.stack.imgur.com/lzL5u.png

我的目标是:

  1. 当我在Y方向上滚动时,我希望滚动视图接管并正常滚动
  2. 当我向X方向滚动时,我希望scrollview下的viewpager接管
  3. 现在的问题是,scrollview会占用所有事件,而且根本无法访问viewpager。

    关于如何解决这个问题的任何想法?

1 个答案:

答案 0 :(得分:4)

对于对此问题的答案感兴趣或有类似问题的任何人,以下是我如何解决它。

首先,一般方法是制作一个自定义视图,拦截某些触摸事件并将它们委托给正确的孩子。

在我的情况下,viewpager应截取水平手势,而viewpager 位于顶部的scrollview应该只获取垂直scroll-events。 对我来说,一个特殊的要求是,viewpager也应该只拦截特定范围内的触摸事件,即虚拟视口,这是一个孩子里面覆盖的滚动视图。

在自定义布局类中,必须与onTouchEvent()方法一起覆盖onInterceptTouchEvent()方法:

@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;
}

如您所见,我总是检查DOWN事件并将其保存在类成员中,一旦检测到移动,我会检查它是否在x方向上执行(对于viewpager)。在那时被截获。我的calculateDelegatesHitRectangle()方法在这种情况下只检查我是否在界限内,除了触摸事件我想要的地方。

在onTouch中,我只将截取的水平手势重新路由到我的viewpager,后者通过setter注入此自定义视图。为了获得正确的手势,我还需要重新路由截获的DOWN事件。

从我的活动代码中我只设置了调度目标和委托视图:

dispatchAwareLayout.setDispatchTargetView(mImageViewPager);
dispatchAwareLayout.setDelegateView(mDummyViewport);