ViewPager 禁止向某个方向滑动

24

我想禁用左右滑动,但只限制在向右侧的滑动。我在这篇答案中找到了可行的解决方案。不幸的是,这种方法复制了整个ViewPager源代码来实现目标。有没有仅继承现有类而不重复代码的方法?


你必须使用原生的ViewPager吗? - Shark
如果有一个包含此行为的ViewPager库,我可以使用它。请注意,我想要动态地启用和禁用向右滑动,而不仅仅是静态地禁用它。 - WonderCsabo
我想你可以尝试重写onTouchEvent或onInterceptTouchEvent方法,并尝试过滤掉你不想它响应的触摸事件(对于其余的事件调用super)。 - Karakuri
实际上我必须复制所有内容,因为我想修改一个私有方法。 :( - WonderCsabo
防止滑动真的很容易,只需一个方法调用。但只防止其中一个方向可能会稍微有点困难... - Shark
创建一个扩展ViewPager的对象并进行自定义工作 ;) - An-droid
7个回答

25

我不确定这是否正是您所需要的: 我需要一个带有最大页数限制的向导视图。

最终解决方案在适配器中。 我更改了PagerAdapter的计数,以此阻止用户超过最大页面:

结果为:

我不确定这是否正是您所需要的: 我需要一个带有最大页数限制的向导视图。

最终解决方案在适配器中。 我更改了 PagerAdapter 的计数,以此阻止用户超过最大页面:

@Override
public int getCount() {
    return mProgress; //max page + 1
}
当用户进入下一页时:
private void setWizardProgress(int progress) {
    if(progress > mProgress) {
        mProgress = progress;
        mWizardPagerAdapter.notifyDataSetChanged();
    }
}

这样当用户到达最大页面时,他就不能向右滚动。


2
谢谢!这是一个老问题,我已经通过放弃“ViewPager”并使用“Fragment”动画来完全控制流程来解决了这个问题。我认为你的解决方案很好,所以我接受它。 :) - WonderCsabo
1
你是怎么做到的?可以分享一段代码吗?用这个例子,你是如何禁用右侧的交换功能的? - badarshahzad

23

这里是具有禁用任何方向翻页功能的工作ViewPager类。查看所有答案

public class CustomViewPager extends ViewPager {
    private float initialXValue;
    private SwipeDirection direction;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.direction = SwipeDirection.all;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.IsSwipeAllowed(event)) {
            return super.onTouchEvent(event);
        }

        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.IsSwipeAllowed(event)) {
            return super.onInterceptTouchEvent(event);
        }

        return false;
    }

    private boolean IsSwipeAllowed(MotionEvent event) {
        if(this.direction == SwipeDirection.all) return true;

        if(direction == SwipeDirection.none )//disable any swipe
            return false;

        if(event.getAction()==MotionEvent.ACTION_DOWN) {
            initialXValue = event.getX();
            return true;
        }

        if(event.getAction()==MotionEvent.ACTION_MOVE) {
            try {
                float diffX = event.getX() - initialXValue;
                if (diffX > 0 && direction == SwipeDirection.right ) {
                    // swipe from left to right detected
                    return false;
                }else if (diffX < 0 && direction == SwipeDirection.left ) {
                    // swipe from right to left detected
                    return false;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }

        return true;
    }

    public void setAllowedSwipeDirection(SwipeDirection direction) {
        this.direction = direction;
    }

这个东西实际上是可以工作的。(尽管语法风格非常糟糕) - Vedant Agarwala
@vedant1811,i有什么不好的地方吗? - lelloman
@lelloman可能指的是缺少自动格式化,IsSwipeAllowed的首字母大写,try-catch中没有抛出异常以及printStackTrace,以及不必要的"this"位置(重写的方法实际上可以内联)。但是答案仍然很有帮助。 - 0101100101
嗨,@andre719mv,这对于普通页面可以正常工作。但是如果页面包含EditText,则触摸将被禁用,因此...在这种情况下,这个解决方案不起作用。 - Chinmay
2
try-catch块是不必要的。里面没有任何东西会抛出异常。 - Egis
显示剩余2条评论

10

另一种简单的方法是使用setCurrentItem()在达到特定位置时滚动回所需的幻灯片。例如,这只允许向前滑动:

另一种简单的方法是使用setCurrentItem()在达到特定位置时滚动回所需的幻灯片。例如,这只允许向前滑动:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}

    @Override
    public void onPageSelected(int position) {
        if(position < mProgress) {
            mViewPager.setCurrentItem(mProgress, true);
        } else {
            mProgress = position;
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {}
});

或者,如果你想要有一个最大的幻灯片:

if(position > 4) {
    mViewPager.setCurrentItem(4, true);
}

这个解决方案在技术上并不会完全禁用滑动,因为当您进行滑动操作时,您仍然会看到一小部分被禁止的幻灯片。但对于某些应用程序而言,这可能更受欢迎。


4
您可以尝试以下步骤: 步骤1:创建一个名为"CustomViewPager"的新自定义类。该类继承自"ViewPager",并包含一个名为"setPagingEnabled"的新定制方法,其目的是根据需要启用/禁用滑动功能。 步骤2:覆盖两个方法:"onTouchEvent"和"onInterceptTouchEvent"。如果要完全禁用分页,则两者都将返回"false"。 步骤3:在布局文件中用自定义类替换"ViewPager"标签。
    <package_name.customviewpager 
     android:id="@+id/customViewPager" 
     android:layout_height="match_parent" 
     android:layout_width="match_parent" />

Step 4: CustomViewPager.java

    public class CustomViewPager extends ViewPager {

private boolean enabled;

public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.enabled = true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (this.enabled && detectSwipeToRight(event)) {
        return super.onTouchEvent(event);
    }

    return false;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    if (this.enabled && detectSwipeToRight(event)) {
        return super.onInterceptTouchEvent(event);
    }

    return false;
}

// To enable/disable swipe 
public void setPagingEnabled(boolean enabled) {
    this.enabled = enabled;
}

// Detects the direction of swipe. Right or left. 
// Returns true if swipe is in right direction
public boolean detectSwipeToRight(MotionEvent event){

 int initialXValue = 0; // as we have to detect swipe to right
 final int SWIPE_THRESHOLD = 100; // detect swipe
 boolean result = false;

        try {                
            float diffX = event.getX() - initialXValue;

                if (Math.abs(diffX) > SWIPE_THRESHOLD ) {
                    if (diffX > 0) {
                        // swipe from left to right detected ie.SwipeRight
                        result = false;
                    } else {
                        // swipe from right to left detected ie.SwipeLeft
                        result = true;
                    }
                }
            } 
         catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

5
我尝试了更新后的答案。 diffX 总是为正数,因此该方法从不返回 true - WonderCsabo
嗨,wonderCsabo:你有没有找到解决方案?我也尝试了很多次,但仍然面临这种问题。等待您的回复。提前致谢。 - Teraiya Mayur
这并不能检测到滑动,它只检测第一个(和每个后续的)运动事件的X坐标是否大于0。这应该始终返回false,因为X始终是正数。 - Hamid
是的,Hamid,由于这个原因它会禁用双向滑动。 - Mansuu....

3
您可以使用方法beginFakeDrag()endFakeDrag()
如果您想禁用滑动,请使用beginFakeDrag(),如果您想再次启用,请使用endFakeDrag()
例如:viewPager.beginFakeDrag();

1
private float initialXValue;
@Override
public boolean onTouchEvent(MotionEvent event) {
    if (this.mEnabled) {
        return super.onTouchEvent(event);
    }
    if(event.getAction()==MotionEvent.ACTION_DOWN){
        initialXValue = event.getX();
    }else if(event.getAction()==MotionEvent.ACTION_MOVE){
        if(detectSwipeToRight(event)){
            System.out.println("right swipe detected");
        }
    }
    return true;
}

private boolean detectSwipeToRight(MotionEvent event) {
    final int SWIPE_THRESHOLD = 100; // detect swipe
    boolean result = false;

    try {
        float diffX = event.getX() - initialXValue;
        if (Math.abs(diffX) > SWIPE_THRESHOLD) {
            if (diffX < 0) {
                // swipe from right to left detected ie.SwipeLeft
                result = true;
            }
        }
    } catch (Exception exception) {
        exception.printStackTrace();
    }
    return result;
}

2
如果您能更详细地解释一下代码,这将是一个更好的答案。 - skrrgwasme

-3

然后尝试使用canScroll方法,我已经使用过它并且被调用了。 - viplezer
从未被调用过 - Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接