此篇文章使用外部拦截法解决View内外滑动方向不一致的冲突问题
1. 问题描述
外部用HorizontalScrollViewEx模拟ViewPager,内部用ListView。上下滑动则ListView滚动,左右滑动则HorizontalScrollView滚动。下面这篇文章已经实现了HorizontalScrollView,这里只解决滑动冲突问题。
4.4 View的示例(二)
2. HorizontalScrollViewEx中拦截事件
public class HorizontalScrollViewEx extends ViewGroup {
private int mChildrenSize;
private int mChildWidth;
private int mChildIndex;
private int mLastX;
private int mLastY;
private int mLastInterceptX = 0;
private int mLastInterceptY = 0;
private Scroller mscroller;
private VelocityTracker mVelocityTracker;
public HorizontalScrollViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
mscroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 这里必须返回false,因为这里如果父View拦截了,子View收不到后续事件,应该让子View拿到Down事件,父View拦截Move事件。
intercepted = false;
if (!mscroller.isFinished()) {
// 没结束,强制结束
mscroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastInterceptX;
int deltaY = y - mLastInterceptY;
// 这里根据条件判断是否需要拦截
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
// 这里返回false,因为up没什么意义了
intercepted = false;
break;
case MotionEvent.ACTION_CANCEL:
break;
}
mLastX = x;
mLastY = y;
mLastInterceptX = x;
mLastInterceptY = y;
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mVelocityTracker.addMovement(event);
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mscroller.isFinished()) {
// 没结束,强制结束
mscroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
// move的时候,调用scrollBy跟着滚动就可以
int deltaX = x - mLastX;
scrollBy(-deltaX, 0);
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
Log.e("aaa","xVelocity="+xVelocity);
if (Math.abs(xVelocity) >= 50) {
// 有速度则根据速度判断上一页或下一页
// 速度为正,则向右正向,左一页
mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
} else {
// 没有速度则根据位置判断上一页或下一页
mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
}
mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
int dx = mChildIndex * mChildWidth - scrollX;
// 使用scroller滚动
smoothScrollBy(dx, 0);
mVelocityTracker.clear();
break;
case MotionEvent.ACTION_CANCEL:
break;
}
mLastX = x;
mLastY = y;
return super.onTouchEvent(event);
}
private void smoothScrollBy(int dx, int dy) {
mscroller.startScroll(getScrollX(), 0, dx, 0, 500);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mscroller.computeScrollOffset()) {
scrollTo(mscroller.getCurrX(), mscroller.getCurrY());
postInvalidate();
}
}
@Override
protected void onDetachedFromWindow() {
// 做回收工作
mVelocityTracker.recycle();
super.onDetachedFromWindow();
}
}