android如何实现两层可滚动view,内层优先响应

尊重他人劳动成果,转载请说明出处:

需求

我们自定义一个可以滑动的MyView,把这个MyView放在ViewPager(ScrollView同理)里面,滑动该MyView。如果MyView可滑动,那么MyView响应滑动;如果MyView不能滑动,那么让外层的ViewPager滑动。

几个不可行的方法

1.在MyView的onTouchEvent方法中,MotionEvent.ACTION_DOWN事件,返回true。

弊端:不能在适当的时候让ViewPager响应滑动。该方案不可取。

由于我们需要根据情况决定是否让父view响应,那么我们需要在滑动一定距离,类似于android认为滑动超过8像素才是滑动。

2.在MyView的onTouchEvent方法中,MotionEvent.ACTION_MOVE事件,如果移动距离>8像素,那么返回true,并且调用android.view.ViewParent.requestDisallowInterceptTouchEvent(boolean disallowIntercept) 方法,不允许父View截断。

优势:有8像素的距离来决定MyView是否响应滑动

弊端:当移动距离>8像素的时候,先执行ViewPager的onInterceptTouchEvent方法,被ViewPager截断,当移动距离>8像素时,不会执行MyView的onTouchEvent方法。该方案不可取。

3..在MyView的onTouchEvent方法中,MotionEvent.ACTION_MOVE事件,,如果移动距离>4像素,那么返回true,并且调用android.view.ViewParent.requestDisallowInterceptTouchEvent(boolean disallowIntercept) 方法,不允许父View截断。

优势:有4像素的距离来决定MyView是否响应滑动

弊端:当滑动距离在4–8像素之间,能达到我们的需求,MyView有权利先决定是否响应滑动。但是4–8像素是很小的一段距离,只有在我们很慢很慢滑动的时候才会出现滑动距离在4–8像素之间。当我们随手一滑,滑动距离超过8像素,那么MyView的onTouchEvent方法也不会出现4–8像素。该方案不可取。

思路

ViewPager的onInterceptTouchEvent方法里,如果滑动超过8像素,那么就截断该事件,如果我们能在ViewPager截断前,先让它的子view进行判断,并有权处理该滚动事件,那么就可满足需求。可是,从touch事件的传递规则,我们知道:touch从父view传递给子view,父view的onInterceptTouchEvent方法必然是先执行的。那么应该如何解决呢?

灵感

如果两个ViewPager嵌套在一起,你用手滑动内层的ViewPager。如果内层的可滑动,那么内层的响应滑动;如果内层的不可滑动,那么外层的响应滑动。

如果两个ScrollView嵌套在一起,你用手滑动内层的ScrollView。内层的Scroll不会响应滑动,滑动只会被外层的ScrollView响应。

可行的方法:

那么就是说ViewPager已经提供了这种功能,内层的可滑动View有滑动时间的优先响应权。

那么它是怎么做到的呢?就让源码来告诉我们答案。

public boolean onInterceptTouchEvent(MotionEvent ev) {final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {if (DEBUG) Log.v(TAG, "Intercept done!");mIsBeingDragged = false;mIsUnableToDrag = false;mActivePointerId = INVALID_POINTER;if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}return false;}if (action != MotionEvent.ACTION_DOWN) {if (mIsBeingDragged) {if (DEBUG) Log.v(TAG, "Intercept returning true!");return true;}if (mIsUnableToDrag) {if (DEBUG) Log.v(TAG, "Intercept returning false!");return false;}}switch (action) {case MotionEvent.ACTION_MOVE: {final int activePointerId = mActivePointerId;if (activePointerId == INVALID_POINTER) {break;}final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);final float x = MotionEventCompat.getX(ev, pointerIndex);final float dx = x – mLastMotionX;final float xDiff = Math.abs(dx);final float y = MotionEventCompat.getY(ev, pointerIndex);final float yDiff = Math.abs(y – mInitialMotionY);if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&canScroll(this, false, (int) dx, (int) x, (int) y)) {mLastMotionX = x;mLastMotionY = y;mIsUnableToDrag = true;return false;}if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {if (DEBUG) Log.v(TAG, "Starting drag!");mIsBeingDragged = true;requestParentDisallowInterceptTouchEvent(true);setScrollState(SCROLL_STATE_DRAGGING);mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :mInitialMotionX – mTouchSlop;mLastMotionY = y;setScrollingCacheEnabled(true);} else if (yDiff > mTouchSlop) {if (DEBUG) Log.v(TAG, "Starting unable to drag!");mIsUnableToDrag = true;}if (mIsBeingDragged) {if (performDrag(x)) {ViewCompat.postInvalidateOnAnimation(this);}}break;}case MotionEvent.ACTION_DOWN: {mLastMotionX = mInitialMotionX = ev.getX();mLastMotionY = mInitialMotionY = ev.getY();mActivePointerId = MotionEventCompat.getPointerId(ev, 0);mIsUnableToDrag = false;mScroller.computeScrollOffset();if (mScrollState == SCROLL_STATE_SETTLING &&Math.abs(mScroller.getFinalX() – mScroller.getCurrX()) > mCloseEnough) {mScroller.abortAnimation();mPopulatePending = false;populate();mIsBeingDragged = true;requestParentDisallowInterceptTouchEvent(true);setScrollState(SCROLL_STATE_DRAGGING);} else {completeScroll(false);mIsBeingDragged = false;}if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY+ " mIsBeingDragged=" + mIsBeingDragged+ "mIsUnableToDrag=" + mIsUnableToDrag);break;}case MotionEventCompat.ACTION_POINTER_UP:onSecondaryPointerUp(ev);break;}if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);return mIsBeingDragged;}第43~49行的代码的作用就是在ViewPager截断前,把分发的权利转移给子view。从起点,到尽头,也许快乐,或有时孤独,

android如何实现两层可滚动view,内层优先响应

相关文章:

你感兴趣的文章:

标签云: