ViewPager禁止滑动和修改滑动速度

  1. 利用ViewPage的PagerTransformer定制页面切换效果
  2. ViewPager动态添加删除及刷新页面
  3. ViewPager打造真正意义的无限轮播
  4. ViewPage 联动效果自带角标
  5. ViewPager禁止滑动和修改滑动速度

1. 简介

实际开发中,我们有时候需要禁止 ViewPager 滑动,和改变 ViewPager 切换页面时的滑动速率。下面总结了 禁止ViewPager滑动和通过Viewpager 的 scroller 修改滑动速度的实现。非常简单。

2. ViewPager 禁止滑动

禁止 ViewPager 滑动,最简单的方式就是拦截 ViewPager 触摸滑动事件。如下代码所示:

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class NoScrollViewPager extends ViewPager {

    private boolean scrollable = true;

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

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

    public void setScrollable(boolean scrollable) {
        this.scrollable = scrollable;
    }

    public boolean isScrollable() {
        return scrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (scrollable) {
            return super.onTouchEvent(ev);
        } else {
            return false;
        }
    }

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

使用时,调用 setScrollable(false) 就可以禁止 ViewPager 滑动。

3. ViewPager 修改滑动速度

我们都知道,ViewPager 切换页面有两种方式:一是,手指触摸滑动切换页面;二是,调用方法 setCurrentItem(int) 切换页面。用手指触摸滑动切换页面时,页面的滑动与手指滑动速度有关,过渡动画很明显,体验很好。调用方法切换页面时,由于页面切换速度较快,过渡动画不明显甚至没有,往往是直接闪过去。当我们需要 ViewPager 自动切换页面,例如自动轮播时,ViewPager 原有的切换速度有时候难以适应我们的需求,尤其在我们定制了 ViewPager 的切换效果时,就更加难以适应我们对交互体验的需求了。这时我们就需要修改这种情况下页面滑动速度了。如何修改页面切换速度呢?咱们深入了解一下 ViewPager。

首先,我们看一下 setCurrentItem(int) 方法的源码:

public void setCurrentItem(int item) {
    mPopulatePending = false;
    setCurrentItemInternal(item, !mFirstLayout, false);
}

调用了 setCurrentItemInternal(int, boolean, boolean) 方法,再看 setCurrentItemInternal(int, boolean, boolean) 方法;

void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
	setCurrentItemInternal(item, smoothScroll, always, 0);
}

继续往下看:

    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (mAdapter == null || mAdapter.getCount() <= 0) {
            setScrollingCacheEnabled(false);
            return;
        }
        if (!always && mCurItem == item && mItems.size() != 0) {
            setScrollingCacheEnabled(false);
            return;
        }

        if (item < 0) {
            item = 0;
        } else if (item >= mAdapter.getCount()) {
            item = mAdapter.getCount() - 1;
        }
        final int pageLimit = mOffscreenPageLimit;
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            // We are doing a jump by more than one page.  To avoid
            // glitches, we want to keep all current pages in the view
            // until the scroll ends.
            for (int i = 0; i < mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
        final boolean dispatchSelected = mCurItem != item;

        if (mFirstLayout) {
            // We don't have any idea how big we are yet and shouldn't have any pages either.
            // Just set things up and let the pending layout handle things.
            mCurItem = item;
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            requestLayout();
        } else {
            populate(item);
            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
        }
    }

整段代码和滑动相关的大概只有这一行:

scrollToItem(item, smoothScroll, velocity, dispatchSelected);

我们看一下此方法:

    private void scrollToItem(int item, boolean smoothScroll, int velocity,
            boolean dispatchSelected) {
        final ItemInfo curInfo = infoForPosition(item);
        int destX = 0;
        if (curInfo != null) {
            final int width = getClientWidth();
            destX = (int) (width * Math.max(mFirstOffset,
                    Math.min(curInfo.offset, mLastOffset)));
        }
        if (smoothScroll) {
            smoothScrollTo(destX, 0, velocity);
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
        } else {
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            completeScroll(false);
            scrollTo(destX, 0);
            pageScrolled(destX);
        }
    }

在此方法中,velocity 表示速度,与速度有关的是 smoothScrollTo(destX, 0, velocity),我们只需关注 smoothScrollTo() 方法。

    void smoothScrollTo(int x, int y, int velocity) {
        if (getChildCount() == 0) {
            // Nothing to do.
            setScrollingCacheEnabled(false);
            return;
        }

        int sx;
        boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();
        if (wasScrolling) {
            // We're in the middle of a previously initiated scrolling. Check to see
            // whether that scrolling has actually started (if we always call getStartX
            // we can get a stale value from the scroller if it hadn't yet had its first
            // computeScrollOffset call) to decide what is the current scrolling position.
            sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();
            // And abort the current scrolling.
            mScroller.abortAnimation();
            setScrollingCacheEnabled(false);
        } else {
            sx = getScrollX();
        }
        int sy = getScrollY();
        int dx = x - sx;
        int dy = y - sy;
        if (dx == 0 && dy == 0) {
            completeScroll(false);
            populate();
            setScrollState(SCROLL_STATE_IDLE);
            return;
        }

        setScrollingCacheEnabled(true);
        setScrollState(SCROLL_STATE_SETTLING);

        final int width = getClientWidth();
        final int halfWidth = width / 2;
        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
        final float distance = halfWidth + halfWidth
                * distanceInfluenceForSnapDuration(distanceRatio);

        int duration;
        velocity = Math.abs(velocity);
        if (velocity > 0) {
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {
            final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
            final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
            duration = (int) ((pageDelta + 1) * 100);
        }
        duration = Math.min(duration, MAX_SETTLE_DURATION);

        // Reset the "scroll started" flag. It will be flipped to true in all places
        // where we call computeScrollOffset().
        mIsScrollStarted = false;
        mScroller.startScroll(sx, sy, dx, dy, duration);
        ViewCompat.postInvalidateOnAnimation(this);
    }

这个方法就是关键了!我们知道 velocity 代表速度,找到 velocity,发现 velocity > 0 时,通过 velocity 和 滑动距离(这个应该是手指滑动的)计算出了一个 duration,与设定的最大时间间隔 MAX_SETTLE_DURATION 比较,取较小的,然后再参与滚动。我们向上找 velocity 的传值,发现在 void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) 方法中我们传了 0,duration 会根据 将要滚动的距离 和 页面占用的宽度 计算出滚动的页数,duration 为 100*(滚动页数 + 1) 毫秒,最后还是会取 duration 和 MAX_SETTLE_DURATION 的较小值。最终,duration 最大为 MAX_SETTLE_DURATION ,MAX_SETTLE_DURATION 的值是 600。而 duration 最终传入了 mScroller.startScroll(sx, sy, dx, dy, duration) 。两个页面切换时,滚动距离是固定的,duration 越小,速度越快;duration 越大,速度越慢。速度越慢,我越是有时间展示我们的页面切换动画,交互体验就越好。

那么,如何改变这个duration 呢?我们还要看 mScroller。发现,mScroller 是 ViewPager 的私有属性,而且没有提供对外的方法去修改这个 mScroller。没有办法,我们只能借助于强大的反射机制,简单粗暴但实用。

既然要改变 mScroller ,那么就需要为 mScroller 重新赋值,我们先继承 Scroller 重写 startScroll() 方法,使用自己的 duration,弃用上面代码传入的 duration 如下代码所示:

import android.content.Context;
import android.view.animation.Interpolator;
import android.widget.Scroller;

public class FixedSpeedScroller extends Scroller {

    private static final int DURATION_DEF = 1500;
    private int mDuration = DURATION_DEF;
                                                                                                            
    public FixedSpeedScroller(Context context) {
        this(context, DURATION_DEF);
    }

    public FixedSpeedScroller(Context context, int duration) {
        this(context, null, duration);
    }
                                                                                                            
    public FixedSpeedScroller(Context context, Interpolator interpolator) {
        this(context, interpolator, DURATION_DEF);
    }
    public FixedSpeedScroller(Context context, Interpolator interpolator, int duration) {
        super(context, interpolator);
        this.mDuration = duration;
    }
                                                                                                            
    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
                                                                                                            
    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        // Ignore received duration, use fixed one instead
        this.startScroll(startX, startY, dx, dy, mDuration);
    }
                                                                                                            
    public void setDuration(int duration) {
        mDuration = duration;
    }

}

这样的话,我们就可以自己自定义滑动的时间,控制滑动的速度。下一步就是如何修改 mScroller值了,上面提到,我们只能使用反射,那我们就用反射为 mScroller 重新赋上我们想要的值,如下代码:

try {
    Field field = ViewPager.class.getDeclaredField("mScroller");
    field.setAccessible(true);
    FixedSpeedScroller scroller = new FixedSpeedScroller(viewPager.getContext(), new AccelerateInterpolator());
    field.set(viewPager, scroller);
    scroller.setDuration(2000);
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

4. 参考

·1> android之ViewPager修改滑动速度:
https://www.cnblogs.com/cmai/p/7705190.html

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以通过以下两种方法禁止ViewPager滑动: 1. 自定义ViewPager类,重写onTouchEvent方法,不处理触摸事件即可。 2. 使用ViewPager的setOnTouchListener方法,监听触摸事件,当触摸事件发生时,返回true,即可禁止ViewPager滑动。 具体实现方法可以参考以下代码: 1. 自定义ViewPager类: ``` public class NoScrollViewPager extends ViewPager { public NoScrollViewPager(Context context) { super(context); } public NoScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent ev) { return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } } ``` 2. 使用setOnTouchListener方法: ``` viewPager.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return true; } }); ``` ### 回答2: Android中的ViewPager是一个非常流行的View,它提供了一种灵活的方式来切换不同的视图。默认情况下,ViewPager可以通过手势水平滑动切换视图。然而,在某些情况下,您可能需要禁止用户手动滑动,而只是依靠代码来处理切换视图。 一种方法是使用ViewPager的setEnabled()方法来禁用ViewPager的手势滑动。将setEnabled()方法的参数设置为false即可。但是这种方法还可以让ViewPager仍然可以通过代码来进行切换视图,这可能并不是您想要的。 另一种方法是,您可以通过自定义ViewPager来禁止手势滑动,但仍保持使用代码来实现视图切换。 自定义ViewPager主要涉及两个方面。 第一方面涉及ViewPager的手势控制,默认情况下ViewPager具有手势控制功能,我们需要禁用这些手势控制。我们可以通过重写ViewPager的onInterceptTouchEvent和onTouchEvent方法来实现。在这两个方法中,我们可以判断用户的操作类型,如果是手势滑动,我们就不需要将事件传递给父View或子View,以此来禁用ViewPager的手势滑动。 第二个方面涉及ViewPager的动画效果。为了保证视图的切换效果,ViewPager默认添加了一些动画效果。而我们禁用了手势滑动后,这些动画效果可能会变得不协调或不适合我们的需求。因此,我们需要重写ViewPager的onPageScrolled方法来取消动画效果。 通过以上两个操作,我们就可以实现自定义ViewPager,并禁用手势滑动。但是需要注意的是,这样可能会影响用户的交互体验,因此在开发中需要根据具体情况慎重考虑是否要禁用手势滑动。 ### 回答3: 在 Android 中,如果想要禁止 ViewPager 的滑动,可以通过以下三种方法实现: 1. 自定义 ViewPager 可以自定义一个继承自 ViewPager 的类,重写它的 onTouchEvent() 方法和 onInterceptTouchEvent() 方法,在这两个方法中判断是否禁止滑动,如果禁止了,则不执行父类的 onTouchEvent() 和onInterceptTouchEvent() 方法。示例代码如下: ``` public class NonSwipeableViewPager extends ViewPager { private boolean isSwipeEnabled = false; public NonSwipeableViewPager(@NonNull Context context) { super(context); } public NonSwipeableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent ev) { return isSwipeEnabled && super.onTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return isSwipeEnabled && super.onInterceptTouchEvent(ev); } public void setSwipeEnabled(boolean enabled) { isSwipeEnabled = enabled; } } ``` 2. 给 ViewPager 设置监听器 可以在 Activity 或 Fragment 中给 ViewPager 设置 onPageChangeListener,然后在 onPageScrollStateChanged() 方法中判断是否禁止滑动,如果禁止了,则将其返回到初始位置。示例如下: ``` viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { int currentItem = viewPager.getCurrentItem(); if (currentItem == 0) { viewPager.setCurrentItem(0, false); } else if (currentItem == viewPager.getAdapter().getCount() - 1) { viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1, false); } } } }); ``` 3. 通过 layout 参数禁止滑动 在 XML 中设置 ViewPager 的 `android:layout_width` 属性为 0dp,可以禁止它的滑动。示例代码如下: ``` <ViewPager android:id="@+id/viewPager" android:layout_width="0dp" android:layout_height="wrap_content" /> ``` 以上是三种禁止 ViewPager 滑动的方法,开发者可以根据实际需求选用合适的方法来实现自己需要的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值