在Android中Viewpager控制器的速度减慢

Ale*_*lly 102 android

用android中的viewpager适配器有没有办法减慢滚动速度?


你知道,我一直在看这段代码.我无法弄清楚我错了什么.

try{ 
    Field mScroller = mPager.getClass().getDeclaredField("mScroller"); 
    mScroller.setAccessible(true); 
    Scroller scroll = new Scroller(cxt);
    Field scrollDuration = scroll.getClass().getDeclaredField("mDuration");
    scrollDuration.setAccessible(true);
    scrollDuration.set(scroll, 1000);
    mScroller.set(mPager, scroll);
}catch (Exception e){
    Toast.makeText(cxt, "something happened", Toast.LENGTH_LONG).show();
} 
Run Code Online (Sandbox Code Playgroud)

它没有改变什么,但没有例外发生?

mar*_*mor 226

我已经开始使用HighFlyer的代码确实改变了mScroller字段(这是一个很好的开始)但是没有帮助延长滚动的持续时间,因为ViewPager在请求滚动时显式地将持续时间传递给mScroller.

扩展ViewPager不起作用,因为重要的方法(smoothScrollTo)无法被覆盖.

我最后通过使用以下代码扩展Scroller来解决这个问题:

public class FixedSpeedScroller extends Scroller {

    private int mDuration = 5000;

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

    public FixedSpeedScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
        super(context, interpolator, flywheel);
    }


    @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
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它像这样:

try {
    Field mScroller;
    mScroller = ViewPager.class.getDeclaredField("mScroller");
    mScroller.setAccessible(true); 
    FixedSpeedScroller scroller = new FixedSpeedScroller(mPager.getContext(), sInterpolator);
    // scroller.setFixedDuration(5000);
    mScroller.set(mPager, scroller);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
Run Code Online (Sandbox Code Playgroud)

我基本上将持续时间硬编码为5秒,并使我的ViewPager使用它.

希望这可以帮助.

  • 你能告诉我什么是sInterpolator? (15认同)
  • "插值器"是动画运行的速率,例如它们可以加速,减速等等.有一个没有插值器的构造函数,我认为应该可行,所以只需尝试从调用中删除该参数.如果这不起作用,请添加:'Interpolator sInterpolator = new AccelerateInterpolator()' (10认同)
  • 我发现DecelerateInterpolator在这种情况下工作得更好,它保持初始滑动的速度然后慢慢减速. (8认同)
  • 如果您正在使用proguard,请不要忘记添加以下行:-keep class android.support.v4.**{*;}否则在getDeclaredField()时您将获得空指针 (6认同)

Ole*_*ich 60

我想做自己并且已经实现了解决方案(但是使用反射).它与已接受的解决方案类似,但使用相同的插值器,仅根据因子更改持续时间.您需要ViewPagerCustomDuration在XML中使用a 而不是ViewPager,然后您可以这样做:

ViewPagerCustomDuration vp = (ViewPagerCustomDuration) findViewById(R.id.myPager);
vp.setScrollDurationFactor(2); // make the animation twice as slow
Run Code Online (Sandbox Code Playgroud)

ViewPagerCustomDuration.java:

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

import java.lang.reflect.Field;

public class ViewPagerCustomDuration extends ViewPager {

    public ViewPagerCustomDuration(Context context) {
        super(context);
        postInitViewPager();
    }

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

    private ScrollerCustomDuration mScroller = null;

    /**
     * Override the Scroller instance with our own class so we can change the
     * duration
     */
    private void postInitViewPager() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            Field interpolator = viewpager.getDeclaredField("sInterpolator");
            interpolator.setAccessible(true);

            mScroller = new ScrollerCustomDuration(getContext(),
                    (Interpolator) interpolator.get(null));
            scroller.set(this, mScroller);
        } catch (Exception e) {
        }
    }

    /**
     * Set the factor by which the duration will change
     */
    public void setScrollDurationFactor(double scrollFactor) {
        mScroller.setScrollDurationFactor(scrollFactor);
    }

}
Run Code Online (Sandbox Code Playgroud)

ScrollerCustomDuration.java:

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

public class ScrollerCustomDuration extends Scroller {

    private double mScrollFactor = 1;

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

    public ScrollerCustomDuration(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    @SuppressLint("NewApi")
    public ScrollerCustomDuration(Context context, Interpolator interpolator, boolean flywheel) {
        super(context, interpolator, flywheel);
    }

    /**
     * Set the factor by which the duration will change
     */
    public void setScrollDurationFactor(double scrollFactor) {
        mScrollFactor = scrollFactor;
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        super.startScroll(startX, startY, dx, dy, (int) (duration * mScrollFactor));
    }

}
Run Code Online (Sandbox Code Playgroud)

希望这有助于某人!

  • 我比接受的答案更喜欢这个解决方案,因为自定义ViewPager是一个插入类,对现有代码的更改很少.将自定义滚动条作为自定义ViewPager的静态内部类使其更加封装. (3认同)

lob*_*zik 41

基于@ df778899的答案Android ValueAnimator API,我找到了更好的解决方案.没有反射它工作正常,非常灵活.此外,无需自定义ViewPager并将其放入android.support.v4.view包中.这是一个例子:

private void animatePagerTransition(final boolean forward) {

    ValueAnimator animator = ValueAnimator.ofInt(0, viewPager.getWidth() - ( forward ? viewPager.getPaddingLeft() : viewPager.getPaddingRight() ));
    animator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            viewPager.endFakeDrag();
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            viewPager.endFakeDrag();
        }

        @Override
        public void onAnimationRepeat(Animator animation) {
        }
    });

    animator.setInterpolator(new AccelerateInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        private int oldDragPosition = 0;

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int dragPosition = (Integer) animation.getAnimatedValue();
            int dragOffset = dragPosition - oldDragPosition;
            oldDragPosition = dragPosition;
            viewPager.fakeDragBy(dragOffset * (forward ? -1 : 1));
        }
    });

    animator.setDuration(AppConstants.PAGER_TRANSITION_DURATION_MS);
    viewPager.beginFakeDrag();
    animator.start();
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这被低估了.它使用了`startFakeDrag`和`endFakeDrag`,它们的功能远远超过`ViewPager`开箱即用的功能.这对我很有用,正是我所寻找的 (7认同)
  • @lobzic,你到底在哪里调用你的`animatePagerTransition(final boolean forward`方法?这就是我很困惑的地方。你是在`ViewPager.OnPageChangeListener`接口方法之一中调用它吗?还是其他地方? (3认同)

Hig*_*yer 17

正如您在ViewPager源中所看到的,由mScroller对象控制的fling持续时间.在文档中,我们可以阅读:

滚动的持续时间可以在构造函数中传递,并指定滚动动画应该采用的最长时间

因此,如果要控制速度,可以通过反射更改mScroller对象.

你应该写这样的东西:

setContentView(R.layout.main);
mPager = (ViewPager)findViewById(R.id.view_pager);
Field mScroller = ViewPager.class.getDeclaredField("mScroller");   
mScroller.setAccessible(true);
mScroller.set(mPager, scroller); // initialize scroller object by yourself 
Run Code Online (Sandbox Code Playgroud)

  • 你应该写这样的东西:setContentView(R.layout.main); mPager =(ViewPager)findViewById(R.id.view_pager); 字段mScroller = ViewPager.class.getDeclaredField("mScroller"); mScroller.setAccessible(真); mScroller.set(mPager,scroller); //自己初始化滚动对象 (12认同)

paw*_*eba 16

这不是完美的解决方案,你不能让速度变慢,因为它是一个int.但对我来说它足够慢,我不必使用反射.

注意类所在的包.smoothScrollTo有包装可见性.

package android.support.v4.view;

import android.content.Context;
import android.util.AttributeSet;

public class SmoothViewPager extends ViewPager {
    private int mVelocity = 1;

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

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

    @Override
    void smoothScrollTo(int x, int y, int velocity) {
        //ignore passed velocity, use one defined here
        super.smoothScrollTo(x, y, mVelocity);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @ElhananMishraky确切地说,这就是我的意思`注意课程所在的包. (3认同)
  • 等等,等等,等等......呜呜?你可以使用sdk的`package-local``field` /`methods`,如果你让Android Studio认为你使用的是同一个包吗?我是唯一闻到一些"密封违规"的人吗? (3认同)

df7*_*899 8

ViewPager上的fakeDrag方法似乎提供了另一种解决方案.

例如,这将从第0项到第1页进行分页:

rootView.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
      ViewPager pager = (ViewPager) getActivity().findViewById(R.id.pager);
      //pager.setCurrentItem(1, true);
      pager.beginFakeDrag();
      Handler handler = new Handler();
      handler.post(new PageTurner(handler, pager));
  }
});


private static class PageTurner implements Runnable {
  private final Handler handler;
  private final ViewPager pager;
  private int count = 0;

  private PageTurner(Handler handler, ViewPager pager) {
    this.handler = handler;
    this.pager = pager;
  }

  @Override
  public void run() {
    if (pager.isFakeDragging()) {
      if (count < 20) {
        count++;
        pager.fakeDragBy(-count * count);
        handler.postDelayed(this, 20);
      } else {
        pager.endFakeDrag();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

(这count * count就是为了让阻力随着时间的推移而加速)