滚动行为:ViewPager中的Horizo​​ntalScrollView

时间:2017-11-04 12:39:48

标签: android user-interface android-viewpager horizontalscrollview

我有一个包含水平滚动视图(HSV)的View Pager(VP)。如果HSV到达其中一个边缘或者根本无法滚动,则在阻塞方向上的新滑动中,VP应该接管滚动到下一页。我对提出这个问题犹豫不决,因为我找到了类似这样的问题:

Can I use Horizontal Scrollview Inside a Viewpager in Android?

horizontalscrollview inside viewpager

但解决方案对我不起作用。 'v instanceof Horizo​​ntalScrollView'变为true但viewPager不滚动

任何其他想法如何实现预期的行为?

public class MyViewPager extends ViewPager {

public MyViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}
// Update 1
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return true;
    //return super.onInterceptTouchEvent(ev);
}

/**
 * https://stackoverflow.com/questions/22781496/can-i-use-horizontal-scrollview-inside-a-viewpager-in-android
 */
@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (v instanceof HorizontalScrollView) {
        return true;
    }
    return super.canScroll(v, checkV, dx, x, y);
}

}

子视图:view_pager_page.xml:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">
            <HorizontalScrollView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center">

                    <include layout="@layout/" />
                </LinearLayout>
            </HorizontalScrollView>
        </LinearLayout>
    </FrameLayout>
</RelativeLayout>

父视图:view_pager.xml

<android.support.design.widget.CoordinatorLayout>
...
<LinearLayout>
<packagepath.MyViewPager
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
</packagepath.MyViewPager>
</LinearLayout>
...
<android.support.design.widget.CoordinatorLayout>

更新1 :当覆盖'onInterceptTouchEvent'并让它始终返回true时,VP滚动,但HSV不会。我认为只有当HSV到达边缘时,这必须返回真的吗?如果是这种情况,我怎么能弄清楚这个方法呢?

更新2 :我重建了android的触摸事件机制,希望能够深入了解如何拦截运动事件流。例如。在HSV中我可以简单地返回false以让VP消耗这个以及所有后续的运动事件。不幸的是,我需要两个MotionEvent.MOVE类型的运动事件来决定HSV或VP是否应该在到达边缘时滚动(如果HSV已经到达右边缘,右侧滑动向后滚动HSV并且向左滑动滚动到VP的下一页)。但是,如果我跳过MotionEvent.DOWN操作,HSV或VP都不会开始滚动......很难解决。有什么想法吗?

Touchevent Mechanism in Android

警告:图片不完整且会包含错误,欢迎所有人更正: - )

更新3 :最后我明白了。理解Touchevent机制有很大帮助,也是ZeroOne的第一个评论。我有空的时候会发布我的解决方案。

2 个答案:

答案 0 :(得分:0)

1.Extend ViewPager Class:

public class ViewPagerContainingHorizontalScrollView extends ViewPager {
private Float x_old;
private boolean bDoIntercept = false;
private boolean bHsvRightEdge = false;
private boolean bHsvLeftEdge = true;


public ViewPagerContainingHorizontalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

private float calculateDistanceSwipe(MotionEvent ev){
    float distance = 0;
    if (x_old == null) {
        x_old = ev.getX();
    } else {
        distance = ev.getX() - x_old;
        x_old = null;
    }
    return distance;
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    mDoIntercept = false;
    if(ev.getAction() == MotionEvent.ACTION_MOVE) {
        float distance = calculateDistanceSwipe(ev);

        if (distance < 0) {//scrolling left direction
            if (bHsvRightEdge) { //HSV right edge
                bDoIntercept = true;
                //When scrolling slow VP may not switch page.
                //Then HSV snaps back into old position.
                //To allow HSV to scroll into non blocked direction set following to false.
                bHsvRightEdge = false;
            }
            bHsvLeftEdge = false;//scrolling left means left edge not reached
        } else if (distance > 0) {//scrolling right direction
            if (bHsvLeftEdge) { //HSV left edge
                bDoIntercept = true;
                //When scrolling slow VP may not switch page.
                //Then HSV snaps back into old position.
                //To allow HSV to scroll into non blocked direction set following to false.
                bHsvLeftEdge = false;
            }
            bHsvRightEdge = false;//scrolling right means right edge not reached
        }
    }
    return super.dispatchTouchEvent(ev);
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if(bDoIntercept){
        return true;
    }else{
        return super.onInterceptTouchEvent(ev);
    }
}

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (v instanceof HorizontalScrollView) {
        HorizontalScrollView hsv = (HorizontalScrollView) v;
        int max_scrollX = hsv.getChildAt(0).getWidth() - hsv.getWidth();
        int min_scrollX = 0;
        int current_scroll_x = hsv.getScrollX();

        if (current_scroll_x == max_scrollX) { //HSV right edge
            bHsvRightEdge = true;
        }

        if (current_scroll_x == min_scrollX) { //HSV left edge
            bHsvLeftEdge = true;
        }
        return true;
    }
    return super.canScroll(v, checkV, dx, x, y);
}
}
  1. 在XML中使用此自定义VP。
  2. 在VP中享受嵌套的HSV滚动: - )
  3. Touch Event Mechanism Overview for this specific case

答案 1 :(得分:0)

我用一个自定义的Horizo​​ntalScrollView解决了这个问题。关键是要覆盖onTouchEvent()方法,如果您在左侧边缘向右滑动,或者在右侧边缘向左滑动,则返回false。返回false表示此视图不使用touch事件,并且此事件可以使视图层次结构冒泡,由ViewPager处理。

public class HorizontalScrollViewForViewPager extends HorizontalScrollView {

float old_x, old_y;

public HorizontalScrollViewForViewPager(Context context) {
    super(context);
}

public HorizontalScrollViewForViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public HorizontalScrollViewForViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    int action = ev.getActionMasked();

    if (action == MotionEvent.ACTION_DOWN) {
        //Start of touch.  Could be tap, could be drag.
        old_x = ev.getX();
        old_y = ev.getY();
    } else if (action == MotionEvent.ACTION_MOVE) {
        //Drag movement underway
        float deltaX = ev.getX() - old_x;
        float deltaY = ev.getY() - old_y;

        if (Math.abs(deltaX) > Math.abs(deltaY)) {
            //scrolling more left/right than up/down
            if (deltaX > 0 && getScrollX() == 0) {
                //dragging left, at left edge of HorizontalScrollView.  Don't handle this touch event, let it bubble up to ViewPager
                return false;
            } else {
                //dragging right.  Use first child to determine width of content inside HorizontalScrollView
                int childWidth = getChildAt(0).getWidth();
                if (deltaX < 0 && (this.getScrollX() + this.getWidth()) >= childWidth) {
                    //swiping left, and at right edge of HorizontalScrollView.  Don't handle this touch event, let it bubble up to ViewPager
                    return false;
                }
            }
        }
    }

    return super.onTouchEvent(ev);
}

}