频道栏目
首页 > 程序开发 > 移动开发 > 其他 > 正文
移动开发滑动事件分发和拦截实现方法
2017-12-13 02:07:46         来源:sinat_29177073的博客  
收藏   我要投稿

移动开发滑动事件分发和拦截实现方法,对于listView,如果它的item可以左右滑动,此时的事件分发分析:

listView继承自AbsListView,它的onInterceptTouchEvent默认返回true,所以在move事件时可以滑动

当它的item可以左右滑动时,根据事件分发的流程,若item的根布局的onInterceptTouchEvent,在move事件时,判断左右有位移时返回true,那么就会拦截事件传递,不响应item里的子view的点击事件,而是响应item根布局的onTouchEvent。进入onTouchEvent后,如果按住进行上下滑动,那么会因为listView的onInterceptTouchEvent默认返回true

从而被listview拦截掉,若想进行左右滑动,则要在move事件里判断左右位移是否大于竖直位移,如果大于,则getParent().requestDisallowInterceptTouchEvent(true);

进行反拦截,将touchevent事件交给item的根布局处理,从而实现左右滑动

/**
     * true:拦截孩子的事件,但会执行当前控件的onTouchEvent()方法
     * false:不拦截孩子的事件,事件继续传递
     * @param event
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercept = false;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //1.按下记录坐标
                downX = startX = event.getX();
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN");
                if(onStateChangeListenter != null){
                    onStateChangeListenter.onDown(this);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE");
                //2.记录结束值
                float endX = event.getX();
                float endY = event.getY();
                //3.计算偏移量
                float distanceX = endX - startX;

                startX = event.getX();
                //在X轴和Y轴滑动的距离
                float DX = Math.abs(endX-downX);
                if(DX>8){
                    intercept = true;
                }


                break;
            case MotionEvent.ACTION_UP:
                break;
        }



        return intercept;
    }
@Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //1.按下记录坐标
                 downX = startX = event.getX();
                 downY = startY = event.getY();
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE");
                //2.记录结束值
                float endX = event.getX();
                float endY = event.getY();
                //3.计算偏移量
                float distanceX = endX - startX;


                int toScrollX = (int) (getScrollX() - distanceX);
                if (toScrollX < 0) {
                    toScrollX = 0;
                } else if (toScrollX > menuWidth) {
                    toScrollX = menuWidth;
                }

                scrollTo(toScrollX, getScrollY());

                startX = event.getX();
                startY = event.getY();
                //在X轴和Y轴滑动的距离
                float DX = Math.abs(endX-downX);
                float DY = Math.abs(endY-downY);
                if(DX > DY&&DX>8){
                    //水平方向滑动
                    //响应侧滑
                    //反拦截-事件给SlideLayout
					//listview recyclerview默认会拦截,所以这里要反拦截
                    getParent().requestDisallowInterceptTouchEvent(true);
                }


                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_UP");
                int totalScrollX = getScrollX();//偏移量
                if(totalScrollX < menuWidth/2){
                    //关闭Menu
                    closeMenu();
                }else{
                    //打开Menu
                    openMenu();
                }
                break;
        }

        return true;
    }


对于RecyclerView,我们可以来看下源码

有这么三种状态:空闲,手指拖动,拖动放开后自然滑行

/**
 * The RecyclerView is not currently scrolling.
 * @see #getScrollState()
 */
public static final int SCROLL_STATE_IDLE = 0;

/**
 * The RecyclerView is currently being dragged by outside input such as user touch input.
 * @see #getScrollState()
 */
public static final int SCROLL_STATE_DRAGGING = 1;

/**
 * The RecyclerView is currently animating to a final position while not under
 * outside control.
 * @see #getScrollState()
 */
public static final int SCROLL_STATE_SETTLING = 2;

初始状态为空闲:

private int mScrollState = SCROLL_STATE_IDLE;

然后进入到onInterceptTouchEven方法中去看看,

有两个判断滑动方向的值,默认为false.它会根据你给RecyclerView设置的layoutManager来改变,如果设置的时竖直方向的,那么canScrollVertically()就会返回true.

final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
final boolean canScrollVertically = mLayout.canScrollVertically();

/**
 * Query if horizontal scrolling is currently supported. The default implementation
 * returns false.
 *
 * @return True if this LayoutManager can scroll the current contents horizontally
 */
public boolean canScrollHorizontally() {
    return false;
}

/**
 * Query if vertical scrolling is currently supported. The default implementation
 * returns false.
 *
 * @return True if this LayoutManager can scroll the current contents vertically
 */
public boolean canScrollVertically() {
    return false;
}

然后接着看,进入down事件,会看到在这里判断和设置RecyclerView的状态,这里就是说,如果RecyclerView是自己靠惯性滑动时,你按下此时会进行反拦截,也就是touch事件时交由RecyclerView自己处理掉了,并且设置状态为滑动状态。

if (mScrollState == SCROLL_STATE_SETTLING) {
    getParent().requestDisallowInterceptTouchEvent(true);
    setScrollState(SCROLL_STATE_DRAGGING);
}

在move事件里,也会判断状态。如果不是滑动状态,即是从空闲状态开始拖动,或者惯性滑动时没有触发down事件,或者惯性滑动后回复空闲状态,此时就会判断RecyclerView是横着滑动还是竖直滑动,不管是什么方向的都会设置startRoll为true,那么就会使RecyclerView的状态变为滑动状态

case MotionEvent.ACTION_MOVE: {
    final int index = e.findPointerIndex(mScrollPointerId);
    if (index < 0) {
        Log.e(TAG, "Error processing scroll; pointer index for id "
                + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
        return false;
    }

    final int x = (int) (e.getX(index) + 0.5f);
    final int y = (int) (e.getY(index) + 0.5f);
    if (mScrollState != SCROLL_STATE_DRAGGING) {
        final int dx = x - mInitialTouchX;
        final int dy = y - mInitialTouchY;
        boolean startScroll = false;
        if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
            mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
            startScroll = true;
        }
        if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
            mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
            startScroll = true;
        }
        if (startScroll) {
            setScrollState(SCROLL_STATE_DRAGGING);
        }
    }
} break;

在UP事件中没有与RecyclerView状态相关的代码,所以可以忽略

在onInterceptTouchEven方法最后返回的是一个判断,判断当前状态是否是滑动状态,如果是则返回true,即拦截子view的事件,自己处理

return mScrollState == SCROLL_STATE_DRAGGING;

那么由DOWN和MOVE事件可看出:

空闲状态,点下去不滑动然后松开,mScrollState不为SCROLL_STATE_DRAGGING,子view处理事件

空闲状态,点下去滑动,触发move事件,状态最后变为SCROLL_STATE_DRAGGING,RecyclerView处理事件

惯性状态,点下去,状态变为SCROLL_STATE_DRAGGING,RecyclerView处理事件

点击复制链接 与好友分享!回本站首页
上一篇:Execution failed for task ':compileDebugAidl'. java.lang.IllegalStateException: aidl is missing
下一篇:移动开发Retrofit和OkHttp的简单配合使用教程
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站