Android scrollview嵌套listview滚动冲突解决3种简单方案(抖音九)

探讨了ScrollView中嵌套ListView导致的滑动冲突问题,提供了三种解决方案:重写ListView的OnMeasure方法、使用外拦截和内拦截法,以及自创的处理方式,通过onTouch和onScroll事件控制。

现象:

1.ListView只会显示一行多一点

2.listview不滚动

我们看到只要ScrollView可以滑动,内部的ListView是不能滑动的

 

在ScrollView中嵌套使用ListView,ListView只会显示一行多一点。两者进行嵌套,即会发生冲突。
由于ListView本身都继承于ScrollView,一旦在ScrollView中嵌套ScrollView,
那么里面的ScrollView高度计算就会出现问题。
我们也就无法得到想要的效果。
下面进入正题,我们将讨论ScrollView中嵌套ListView情况。
核心解决方案: 重写ListView或者GridView的OnMesure 方法。对GridView同样适用。

public class Mylistview extends ListView {


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

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

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

    //解决listview高度问题;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        int me = MeasureSpec.makeMeasureSpec(600, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, me);
    }
}

1.为什么listview没有滑动。

但是却又onTouch和onScrollview的监听事件

说明事件的消费不消费和是ontouchEvent,有关

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    listView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
        @Override
        public void onScrollChange(View view, int i, int i1, int i2, int i3) {


            Log.d("MainActivity","onScrollChange");
        }
    });
}


listView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        Log.d("MainActivity","onTouch");
        return false;
    }
});

 

 

得根据实际的需求来确定拦截的规则。这里我们的需求是当ListView滑到顶部了,并且继续向下滑就让ScrollView拦截掉;

当ListView滑到底部了,并且继续向下滑,就让ScrollView拦截掉,其余时候都交给ListView自身处理事件。

什么时候拦截:要想要父类滑动,就拦截,否则不拦截

解决办法

1.外拦截

 

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int y = (int) event.getY();

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN: {
                nowY = y;
                intercepted = super.onInterceptTouchEvent(event);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if(myListView.getFirstVisiblePosition()==0
                        && y>nowY){
                    intercepted = true;
                    break;
                }
                else if(myListView.getLastVisiblePosition()==myListView.getCount()-1
                        && y<nowY){
                    intercepted = true;
                    break;
                }
                intercepted = false;
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }

        return intercepted;
    }
}

2.内部拦截法

先重写ScrollView的onInterceptTouchEvent方法,让其拦截除了Down事件以外的其他方法:

<span style="color:#000000"><span style="color:#abb2bf"><code><span style="color:#61aeee">@Override</span>
    <span style="color:#c678dd">public</span> <span style="color:#c678dd">boolean</span> <span style="color:#61aeee">onInterceptTouchEvent</span>(MotionEvent event) {
        <span style="color:#c678dd">int</span> x = (<span style="color:#c678dd">int</span>) event.getX();
        <span style="color:#c678dd">int</span> y = (<span style="color:#c678dd">int</span>) event.getY();
        <span style="color:#c678dd">int</span> action = event.getAction();
        <span style="color:#c678dd">if</span> (action == MotionEvent.ACTION_DOWN) {
            mLastX = x;
            mLastY = y;
            <span style="color:#c678dd">return</span> <span style="color:#c678dd">super</span>.onInterceptTouchEvent(event);
        } <span style="color:#c678dd">else</span> {
            <span style="color:#c678dd">return</span> <span style="color:#c678dd">true</span>;
        }
    }</code>
</span></span>

来源: https://www.jianshu.com/p/057832528bdd

在重写ListView的dispatchTouchEvent方法,规则已经说明过了:

<span style="color:#000000"><span style="color:#abb2bf"><code> <span style="color:#61aeee">@Override</span>
<span style="color:#c678dd">public</span> <span style="color:#c678dd">boolean</span> <span style="color:#61aeee">dispatchTouchEvent</span>(MotionEvent event) {
    <span style="color:#c678dd">int</span> y = (<span style="color:#c678dd">int</span>) event.getY();

    <span style="color:#c678dd">switch</span> (event.getAction()) {
        <span style="color:#c678dd">case</span> MotionEvent.ACTION_DOWN: {
            nowY = y;
            mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(<span style="color:#c678dd">true</span>);
            <span style="color:#c678dd">break</span>;
        }
        <span style="color:#c678dd">case</span> MotionEvent.ACTION_MOVE: {
            <span style="color:#c678dd">if</span>(<span style="color:#c678dd">this</span>.getFirstVisiblePosition()==<span style="color:#d19a66">0</span>
                    && y>nowY){
                mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(<span style="color:#c678dd">false</span>);
                <span style="color:#c678dd">break</span>;
            }
            <span style="color:#c678dd">else</span> <span style="color:#c678dd">if</span>(<span style="color:#c678dd">this</span>.getLastVisiblePosition()==<span style="color:#c678dd">this</span>.getCount()-<span style="color:#d19a66">1</span>
                    && y<nowY){
                mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(<span style="color:#c678dd">false</span>);
                <span style="color:#c678dd">break</span>;
            }
            mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(<span style="color:#c678dd">true</span>);
            <span style="color:#c678dd">break</span>;
        }
        <span style="color:#c678dd">case</span> MotionEvent.ACTION_UP: {
            <span style="color:#c678dd">break</span>;
        }
        <span style="color:#c678dd">default</span>:
            <span style="color:#c678dd">break</span>;
    }
    
    <span style="color:#c678dd">return</span> <span style="color:#c678dd">super</span>.dispatchTouchEvent(event);
}</code></span></span>

 

方法三:自己独创

原理:listview不滚动,但是ontouch事件还是有的。

1.listviewOntouch事件处理

    listView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            Log.d("MainActivity","onTouch");
            if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                //点击listview里面滚动停止时,scrollview拦截listview的触屏事件,就是scrollview该滚动了
                mysrollview.requestDisallowInterceptTouchEvent(false);
            } else {
                //当listview在滚动时,不拦截listview的滚动事件;就是listview可以滚动,
                mysrollview.requestDisallowInterceptTouchEvent(true);
            }

            return false;
        }
    });
}

2.listview的onScollview方法,不是setOnScrollChangeListener方法哦

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {

    }

    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if((firstVisibleItem+visibleItemCount)==totalItemCount){
            //如果listview部分加载到最后一条了 ,拦截listview的触屏事件,意思就是scrollview可以滚动了;
            mysrollview.requestDisallowInterceptTouchEvent(false);




        }

    }
});

 

 

总结:

其实现在都用RecyclerView代替ListView,ScrollView嵌套RecyclerView时,对于手势已经支持得很好,不必自己处理冲突。这个在demo里也有写,做个对比

 

但是实际却没有达到效果,查了说mScrollView.scrollTo(x, y)首次初始化时无效果。
最后我用了mScrollView.smoothScrollTo(0,0);

 

1.ScrollView嵌套ListView手势冲突

2.scrollview嵌套listview滚动冲突解决方案;

3.Android事件分发机制及滑动冲突解决方案

4.View的事件分发机制和滑动冲突解决方案(优秀)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值