我相信有两种方法可以实现这个目标,我已经通过 ViewPager 的fakeDrag() 尝试过,但它并不完美,幸运的是,另一种方法可以,通过模拟触摸运动事件并使用 ObjectAnimator 指定动画持续时间然后使控制滚动速度变为真。
public class ViewPagerActivity extends FragmentActivity
implements View.OnClickListener, Animator.AnimatorListener {
private ViewPager mViewPager;
private View btnTriggerNext;
private View btnTriggerPrev;
@Override
protected void onCreate(...) {
super...;
setContentView(R.layout.layout_xml);
mViewPager = findViewById(...);
btnTriggerNext = findViewById(R.id.btnTriggerNext);
btnTriggerNext.setOnClickListener(this);
btnTriggerPrev = findViewById(R.id.btnTriggerPrev);
btnTriggerPrev.setOnClickListener(this);
}
private boolean mIsInAnimation;
private long mMotionBeginTime;
private float mLastMotionX;
@Override
public void onClick(View v) {
if (mIsInAnimation) return;
ObjectAnimator anim;
if (v == btnTriggerPrev) {
if (!hasPrevPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, mViewPager.getWidth());
}
else if (v == btnTriggerNext) {
if (!hasNextPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, -mViewPager.getWidth());
}
else return;
anim.setInterpolator(new LinearInterpolator());
anim.addListener(this);
anim.setDuration(300);
anim.start();
}
public void setMotionX(float motionX) {
if (!mIsInAnimation) return;
mLastMotionX = motionX;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_MOVE, mMotionBeginTime, time);
}
@Override
public void onAnimationEnd(Animator animation) {
mIsInAnimation = false;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_UP, mMotionBeginTime, time);
}
@Override
public void onAnimationStart(Animator animation) {
mLastMotionX = 0;
mIsInAnimation = true;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_DOWN, time, time);
mMotionBeginTime = time;
}
// method from http://stackoverflow.com/a/11599282/1294681
private void simulate(int action, long startTime, long endTime) {
// specify the property for the two touch points
MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
pp.id = 0;
pp.toolType = MotionEvent.TOOL_TYPE_FINGER;
properties[0] = pp;
// specify the coordinations of the two touch points
// NOTE: you MUST set the pressure and size value, or it doesn't work
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
pc.x = mLastMotionX;
pc.pressure = 1;
pc.size = 1;
pointerCoords[0] = pc;
final MotionEvent ev = MotionEvent.obtain(
startTime, endTime, action, 1, properties,
pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0);
mViewPager.dispatchTouchEvent(ev);
}
private boolean hasPrevPage() {
return mViewPager.getCurrentItem() > 0;
}
private boolean hasNextPage() {
return mViewPager.getCurrentItem() + 1 < mViewPager.getAdapter().getCount();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
因为它是模拟触摸事件,所以请使用适当的持续时间(小于 600ms 会很好)进行滚动,当滚动进行时,放下手指会停止滚动并导致一些错误。