使用Android的SlidingPaneLayout和ViewPager

我正在尝试像ViewPager一样使用SlidingPaneLayout

<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scientific_graph_slidingPaneLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- The first child view becomes the left pane. --> <ListView android:id="@+id/left_pane" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="left" /> <!-- The second child becomes the right (content) pane. --> <android.support.v4.view.ViewPager android:id="@+id/scientific_graph_viewPager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> </android.support.v4.widget.SlidingPaneLayout> 

当我从左边拉动时,SlidingPaneLayout滑动; 然而,当我从右边拉时,我似乎无法让ViewPager滑动。 当我从右边缘拉,它滑动很less,然后反弹。

这样做甚至可能吗? 有一个更好的方法吗?

我发现通过将手指向上移动到左侧,我可以滑动视图寻呼机。

Solutions Collecting From Web of "使用Android的SlidingPaneLayout和ViewPager"

根本原因是#onInterceptTouchEvent的实现。 较老的SlidingPaneLayout实现调用#canScroll,它将检查触摸目标是否可以滚动,如果是,将滚动触摸目标而不是滑动面板。 除了X拖动超过坡度并且Y拖动超过X拖动(如由OP指出)的情况​​以外,最近的实现看起来总是拦截运动事件,一旦拖动阈值超过坡度。

对此的一个解决scheme是复制SlidingPaneLayout并进行一些更改以使其工作。 这些变化是:

  1. 修改#onInterceptTouchEvent中的ACTION_MOVE大小写以检查#canScroll,

     if (adx > slop && ady > adx || canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) { ... } 
  2. 将#canScroll中的最终检查修改为特殊情况ViewPager。 这个修改也可以通过覆盖#canScroll在子类中完成,因为它不访问任何私有状态。

     protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { ... /* special case ViewPagers, which don't properly implement the scrolling interface */ return checkV && (ViewCompat.canScrollHorizontally(v, -dx) || ((v instanceof ViewPager) && canViewPagerScrollHorizontally((ViewPager) v, -dx))) } boolean canViewPagerScrollHorizontally(ViewPager p, int dx) { return !(dx < 0 && p.getCurrentItem() <= 0 || 0 < dx && p.getAdapter().getCount() - 1 <= p.getCurrentItem()); } 

通过修复ViewDragHelper可能会有更好的方法来实现这一点,但这是Google在未来的支持包更新中应该解决的问题。 上面的黑客应该得到与ViewPagers(和其他水平滚动容器?)的布局现在。

基于@Brien Colwell的解决scheme,我编写了一个自定义的SlidingPaneLayout子类来处理这个问题,并且还添加了边缘滑动,这样当用户向右滚动时,他们不必一直向后滚动向左打开窗格。

由于这是SlidingPaneLayout的子类,因此不需要在Java中更改任何引用,只要确保实例化此类的实例(通常在您的XML中)即可。

 package com.ryanharter.android.view; import android.content.Context; import android.support.v4.view.MotionEventCompat; import android.support.v4.widget.SlidingPaneLayout; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewConfiguration; /** * SlidingPaneLayout that, if closed, checks if children can scroll before it intercepts * touch events. This allows it to contain horizontally scrollable children without * intercepting all of their touches. * * To handle cases where the user is scrolled very far to the right, but should still be * able to open the pane without the need to scroll all the way back to the start, this * view also adds edge touch detection, so it will intercept edge swipes to open the pane. */ public class PagerEnabledSlidingPaneLayout extends SlidingPaneLayout { private float mInitialMotionX; private float mInitialMotionY; private float mEdgeSlop; public PagerEnabledSlidingPaneLayout(Context context) { this(context, null); } public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); ViewConfiguration config = ViewConfiguration.get(context); mEdgeSlop = config.getScaledEdgeSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (MotionEventCompat.getActionMasked(ev)) { case MotionEvent.ACTION_DOWN: { mInitialMotionX = ev.getX(); mInitialMotionY = ev.getY(); break; } case MotionEvent.ACTION_MOVE: { final float x = ev.getX(); final float y = ev.getY(); // The user should always be able to "close" the pane, so we only check // for child scrollability if the pane is currently closed. if (mInitialMotionX > mEdgeSlop && !isOpen() && canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) { // How do we set super.mIsUnableToDrag = true? // send the parent a cancel event MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL); return super.onInterceptTouchEvent(cancelEvent); } } } return super.onInterceptTouchEvent(ev); } }