Android滑动冲突解决

文章介绍了Android中处理事件分发和滑动冲突的两种方法:外部拦截法和内部拦截法。通过示例展示了当ScrollView内嵌套ListView时,如何根据业务逻辑判断滑动方向来决定事件应由哪个组件处理,从而解决同方向滑动冲突。最终给出了实现这一功能的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:View的事件分发机制和滑动冲突解决方案

Android事件冲突处理方案无非两种:内部拦截法和外部拦截法
外部拦截法伪代码:

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //外部拦截法
        boolean intercept = false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                intercept = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (父容器需要事件) {
                    intercept = true;
                } else {
                    intercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
            default:
                break;
        }
        return intercept;
    }

内部拦截法伪代码:
主要通过requestDisallowInterceptTouchEvent(),该方法可以让父容器的事件拦截失效,那么如果父View的拦截失效,则子View即可获得事件

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                  getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (父容器需要事件){
                  getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }


由于父容器的down事件不受requestDisallowInterceptTouchEvent()影响(因为在down事件拦截前会执行重置操作),而down事件如果被父容器拦截,则后续事件都由父容器处理,因此需要在父容器的事件拦截中实现如下代码才能完成内部拦截法的全过程

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //内部拦截法
        if (ev.getAction() == MotionEvent.ACTION_DOWN){
           return false;
        }
        return true;
    }


接下来我们通过一个例子来演示事件冲突的处理,该例子中就是父容器ScrollView中嵌套了ListView,同方向的滑动冲突,布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ScrollView
        android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_200"
                android:text="textView1"
                android:textColor="@color/white" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_700"
                android:text="textView2"
                android:textColor="@color/white" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_200"
                android:text="textView1"
                android:textColor="@color/white" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_700"
                android:text="textView2"
                android:textColor="@color/white" />

            <ListView
                android:id="@+id/lv"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:background="@color/purple_200"
                android:text="textView1"
                android:textColor="@color/white" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_700"
                android:text="textView2"
                android:textColor="@color/white" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_200"
                android:text="textView1"
                android:textColor="@color/white" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="@color/purple_700"
                android:text="textView2"
                android:textColor="@color/white" />
        </LinearLayout>
    </ScrollView>

</android.support.constraint.ConstraintLayout>


在不处理滑动冲突时,效果如下,ScrollView可以滑动,ListView不能滑动

下面需要处理事件冲突,因为外层的ScrollView和内层的ListView都是竖向滑动,因此我们要根据业务逻辑来处理事件的分发,业务逻辑为:当向下滑动,ListView已经滑到顶时,接下来滑动事件给到ScrollView;当向上滑动,ListView已经滑到底时,接下来滑动事件给到父容器;其他时候事件交给ListView自己处理

外部拦截处理代码:

    int pointY = 0;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //外部拦截法
        boolean intercept = false;
        int nowY = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                pointY = nowY;
                intercept = super.onInterceptTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                if (mListView.getFirstVisiblePosition() == 0 && nowY > pointY) {
                    //下滑到顶 继续下滑                    
                    intercept = true;
                } else if (mListView.getLastVisiblePosition() == mListView.getCount() - 1 && nowY < pointY) {
                    //上滑到底 继续上滑
                    intercept = true;
                } else {
                    intercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
            default:
                break;
        }
        return intercept;
    }

    public void setListView(ListView listView) {
        this.mListView = listView;
    }

 内部拦截处理代码:

    int pointY;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int  nowY = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                pointY = nowY;
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (getFirstVisiblePosition() == 0 && nowY>pointY){
                    //在顶 继续下滑
                    getParent().requestDisallowInterceptTouchEvent(false);
                }else if (getLastVisiblePosition() == getCount()-1 && nowY<pointY){
                    //在底 继续上滑
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

 父容器的onInterceptTouchEvent也需要处理

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //内部拦截法
        if (ev.getAction() == MotionEvent.ACTION_DOWN){
            super.onInterceptTouchEvent(ev);
            return false;
        }
        return true;
    }

处理后的效果如下:

项目代码:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值