public class MyViewPager extends ViewGroup {
/**
* 手势识别器
* 在onTouchEvent方法中把事件传递给手势识别器
*/
private GestureDetector detector;
/**
* 当前页面的下标位置
*/
private int currentIndex;
/**
* 起始坐标位置
*/
private float startX;
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(final Context context) {
/**
* 实例化手势识别器
*/
detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
/**
* 滑动
* @param e1 按下
* @param e2 放开
* @param distanceX 横坐标滑动的距离
* @param distanceY 纵坐标滑动的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
/**
* 通过此方法实现滑动
*/
scrollBy((int) distanceX, 0);
return true;
}
});
}
/**
* 布局
*
* @param changed
* @param l 左上角横坐标
* @param t 左上角纵坐标
* @param r 右下角横坐标
* @param b 右下角纵坐标
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View childAt = getChildAt(i);
childAt.layout(i * getWidth(), 0, (i + 1) * getWidth(), getHeight());
}
}
/**
* 测量
* 根据widthMeasureSpec求得宽度width和父View给的模式
* 根据自身宽度width和自身的padding值相减,求得子view可以获得的宽度newWidth
* 根据newWidth和模式求得一个新的MeasureSpec值:
* MeasureSpec.makeMeasureSpec(newSize,newMode);
* 用新的MeasureSpec来计算子View
*
* @param widthMeasureSpec 父层视图给当前视图的宽和模式
* @param heightMeasureSpec 系统测量时测量多次
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int sizeW = MeasureSpec.getSize(widthMeasureSpec);
int modeW = MeasureSpec.getMode(widthMeasureSpec);
int sizeH = MeasureSpec.getSize(heightMeasureSpec);
int modeH = MeasureSpec.getMode(heightMeasureSpec);
Log.e("sizeW=", sizeW + "");
Log.e("modeW=", modeW + "");
Log.e("sizeH=", sizeH + "");
Log.e("modeH=", modeH + "");
for (int i = 0; i < getChildCount(); i++) {
View childAt = getChildAt(i);
childAt.measure(widthMeasureSpec, heightMeasureSpec);
}
}
/**
* 事件的分发
* Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件
* 还是继续分发给子控件处理
* 返回true表示不继续分发,事件没有被消费,返回false则继续向下分发
* 如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截
*
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
private float downX;
private float downY;
/**
* 事件的拦截
* 该方法是ViewGroup特有的方法,负责事件的拦截
* 返回true表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理
* 返回false则不拦截继续往下传递
*
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
detector.onTouchEvent(ev);
boolean result = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("onInterceptTouchEvent", "ACTION_DOWN");
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.e("onInterceptTouchEvent", "ACTION_MOVE");
float endX = ev.getX();
float endY = ev.getY();
float distanceX = Math.abs(endX - downX);
float distanceY = Math.abs(endY - downY);
if (distanceX > distanceY && distanceX > 5) {
result = true;
}
break;
case MotionEvent.ACTION_UP:
Log.e("onInterceptTouchEvent", "ACTION_UP");
break;
}
return result;
}
/**
* 触摸事件
* 用于事件的处理,返回true表示消费处理当前事件,返回false则不处理
* 交给子控件继续分发
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
detector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("onTouchEvent", "ACTION_DOWN");
startX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
Log.e("onTouchEvent", "ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e("onTouchEvent", "ACTION_UP");
float endX = event.getX();
int tmpIndex = currentIndex;
if ((startX - endX) > getWidth() / 2) {
tmpIndex++;
} else if ((endX - startX) > getWidth() / 2) {
tmpIndex--;
}
/**
* 根据下标位置移动到指定页面
*/
scrollToPager(tmpIndex);
break;
}
return true;
}
/**
* 屏蔽非法值,根据位置移动到指定页面
*
* @param tmpIndex
*/
private void scrollToPager(int tmpIndex) {
if (tmpIndex < 0) {
tmpIndex = 0;
}
if (tmpIndex > getChildCount() - 1) {
tmpIndex = getChildCount() - 1;
}
currentIndex = tmpIndex;
/**
* 手放开后自动完成滑动
*
*/
/**
* 使用数值发生器完成剩下的滑动
*/
ValueAnimator valueAnimator = ValueAnimator.ofInt(getScrollX(), currentIndex * getWidth());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
scrollTo(value, 0);
}
});
valueAnimator.setDuration(500).start();
}
}