您的位置:首页 > 其它

如何解决listView或scrollView+viewpager手势冲突的问题

2015-09-08 12:16 597 查看
因为我在项目中遇到了这样一个问题:我的主页面是一个scrollview,里面包含了一个横向显示图片的listview,在滑动横向listview的时候scrollview也在微微上下滑动,此时的横向listview滑动起来也是出现非常卡顿的情况。因此我百度看了很多资料,很多都讲了一大篇的理论,看得头晕眼花。我后来总结了一下,实际怎么解决这样的问题;

首先,每个listview或则scrollview的源代码中有这么一段代码,如下:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev){
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onMotionEvent will be called and we do the actual
         * scrolling there.
         */

        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }

        /*
         * Don't try to intercept touch if we can't scroll anyway.
         */
        if (getScrollY() == 0 && !canScrollVertically(1)) {
            return false;
        }

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */

                /*
                * Locally do absolute value. mLastMotionY is set to the y value
                * of the down event.
                */
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // If we don't have a valid id, the touch down wasn't on content.
                    break;
                }

                final int pointerIndex = ev.findPointerIndex(activePointerId);
                if (pointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + activePointerId
                            + " in onInterceptTouchEvent");
                    break;
                }

                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop) {
                    mIsBeingDragged = true;
                    mLastMotionY = y;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    if (mScrollStrictSpan == null) {
                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                    }
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                final int y = (int) ev.getY();
                if (!inChild((int) ev.getX(), (int) y)) {
                    mIsBeingDragged = false;
                    recycleVelocityTracker();
                    break;
                }

                /*
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
                mLastMotionY = y;
                mActivePointerId = ev.getPointerId(0);

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);
                /*
                * If being flinged and user touches the screen, initiate drag;
                * otherwise don't.  mScroller.isFinished should be false when
                * being flinged.
                */
                mIsBeingDragged = !mScroller.isFinished();
                if (mIsBeingDragged && mScrollStrictSpan == null) {
                    mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                }
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                /* Release the drag */
                mIsBeingDragged = false;
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
                    postInvalidateOnAnimation();
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }

        /*
        * The only time we want to intercept motion events is if we are in the
        * drag mode.
        */
        return mIsBeingDragged;
    }
其实大家只要知道这个方法是事件拦截就行了,关于他的具体机制去看看这篇博客点击打开链接,写得很详细,好了我们接下来就要在我们的自定义组件中覆写这个方法就行了,一切就是这么简单。我实现的是在scrollview中嵌套一个横向滑动的ListView,先来看看我复写的ScrollView:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class MyScrollView extends ScrollView {

	private float mDX, mDY, mLX, mLY;
	int mLastAct = -1;
	boolean mIntercept = false;

	public MyScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}

	public MyScrollView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev){
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN :
			mDX = mDY = 0f;
			mLX = ev.getX();
			mLY = ev.getY();

			break;

		case MotionEvent.ACTION_MOVE :
			final float curX = ev.getX();
			final float curY = ev.getY();
			mDX += Math.abs(curX - mLX);
			mDY += Math.abs(curY - mLY);
			mLX = curX;
			mLY = curY;

			if (mIntercept && mLastAct == MotionEvent.ACTION_MOVE) {
				return false;
			}

			if (mDX > mDY) {

				mIntercept = true;
				mLastAct = MotionEvent.ACTION_MOVE;
				return false;
			}
                  
                      }
                   mLastAct = ev.getAction();
                   mIntercept = false;
                  return super.onInterceptTouchEvent(ev);}



好了,接下来我为大家详细讲解这个方法的具体含义;首先,这方法是View的事件拦截,如果返回true的话,说明触摸事件(包括DOWN,MOVE,UP)不会往下面传递,就在该view上面处理了,怎么理解呢?例如我的ScrollView是A ,横向的ListView是B, B包含在A中,B是A的子view。那么触摸事件是先让A判断了,然后再传给B。如果在A中实现上面那个事件拦截事件,在特定情况下才将事件传给B处理,如果不是B的事件,那个A就不会传给B,这样A和B就分离开了。

其中

if (mDX > mDY)
 {

	mIntercept = true;
	mLastAct = MotionEvent.ACTION_MOVE;
        return false;
}
就是判断 手指在屏幕上面滑动的 方向,这里判断出是在横向上面滑动,那么就返回false,表示不拦截这个事件,这个事件就会传到B,让B来处理这个事件,刚好B是横向的Listview,就会对横向方向的滑动事件进行处理。如果手指滑动的方向不是横向,就supper父类的默认拦截事件。这里就相当于给拦截事件增加了一些判断,而增加的判断无非就是手指的滑动方向,ListView或ScrollView都是竖向的,而横向的ListView和横向滑动的Veiwpager是横向的,不管是谁嵌套谁,只需要在重写第一个View,把它的
public boolean onInterceptTouchEvent(MotionEvent ev)
方法重新写一下,就可以解决手势冲突的问题!

希望对大家的学习和工作有所帮助,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: