View类:
package com.moonlight;
import java.lang.reflect.Field;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Scroller;
import com.moonlight.R;
/**
* @ClassName: SensitiveViewPager
* @Description: 可调节灵敏度的ViewPager
* @author: moonlight
* @date: 2016-8-13 下午3:20:59
*/
public class SensitiveViewPager extends ViewPager {
private VelocityTracker tracker;
private float snap_velocity = 600;
private float snap_distance_percent = 0.3f;
private int downX;
private final String TAG = "SensitiveViewPager";
private int snap_duration = 250;
private FixedSpeedScroller scroller;
public SensitiveViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.sensitive_viewpager);
for (int i = 0; i < a.getIndexCount(); i++) {
switch (a.getIndex(i)) {
case R.attr.snap_velocity:
snap_velocity = a.getDimension(R.attr.snap_velocity, snap_velocity);
break;
case R.attr.snap_distance_percent:
snap_distance_percent = a.getFloat(R.attr.snap_distance_percent, snap_distance_percent);
break;
case R.attr.snap_duration:
snap_duration = a.getInteger(R.attr.snap_duration, snap_duration);
break;
default:
break;
}
}
a.recycle();
//change snap duration
try {
Field field = ViewPager.class.getDeclaredField("mScroller");
field.setAccessible(true);
scroller = new FixedSpeedScroller(getContext(), new AccelerateInterpolator());
field.set(this, scroller);
scroller.setDuration(snap_duration);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
public static class FixedSpeedScroller extends Scroller {
private int mDuration;
public FixedSpeedScroller(Context context) {
super(context);
}
public FixedSpeedScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
@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);
}
public void setDuration(int time) {
mDuration = time;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
if (!scroller.isFinished()) {
scroller.abortAnimation();
}
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int currentItem = getCurrentItem();
if (tracker == null) {
tracker = VelocityTracker.obtain();
}
tracker.addMovement(event);
boolean isHandle = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
break;
case MotionEvent.ACTION_UP:
tracker.computeCurrentVelocity(1000);
int pageCount = getAdapter() == null ? 0 : getAdapter().getCount();
float XVelocity = tracker.getXVelocity();
if (Math.abs(XVelocity) > snap_velocity) {
Log.d(TAG, "XVelocity:" + XVelocity + " currentItem:" + currentItem + " pageCount:" + pageCount);
if (XVelocity > 0 && currentItem > 0) {
setCurrentItem(currentItem - 1, true);
isHandle = true;
} else if (XVelocity < 0 && currentItem < pageCount - 1) {
setCurrentItem(currentItem + 1, true);
isHandle = true;
}
} else {
int curX = (int) event.getX();
int snapDistance = curX - downX;
if (Math.abs(snapDistance) > getWidth() * snap_distance_percent) {
Log.d(TAG, "snapDistance:" + snapDistance + " currentItem:" + currentItem + " pageCount:"
+ pageCount + " downX:" + downX + " curX:" + curX);
if (currentItem > 0 && snapDistance > 0) {
setCurrentItem(currentItem - 1, true);
isHandle = true;
} else if (snapDistance < 0 && currentItem < pageCount - 1) {
setCurrentItem(currentItem + 1, true);
isHandle = true;
}
}
}
if (tracker != null) {
tracker.recycle();
tracker = null;
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_CANCEL:
if (tracker != null) {
tracker.recycle();
tracker = null;
}
break;
default:
break;
}
return !isHandle ? super.onTouchEvent(event) : true;
}
}
attrs.xml中添加属性:
<declare-styleable name="sensitive_viewpager">
<attr name="snap_velocity" format="float"></attr>
<attr name="snap_distance_percent" format="float"></attr>
<attr name="snap_duration" format="integer"></attr>
</declare-styleable>
使用:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
>
<com.moonlight.SensitiveViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical"
app:snap_velocity ="500"
app:snap_distance_percent ="0.2"
app:snap_duration="200"
/>
</FrameLayout>