完美解决Android中的ScrollView嵌套ScrollView滑动冲突问题

滑动冲突在Android中是一个比较常见的问题,尤其是在一些效果比较复杂的页面会经常遇到,今天我就ScrollView嵌套ScrollView滑动冲突问题,从问题解析、解决思路、实际代码来一步步给大家进行讲解;

  • 问题解析:
    追根溯源,滑动冲突会产生的根本原因在于滑动没有被正确的执行者执行,属于内部的滑动被外部劫持消耗掉了。
    要想理解这个问题需要我们对Android中的事件分发机制有所了解,如果对此机制不了解的伙伴可以查看这篇博客—包你看懂Android中的事件分发机制

  • 那么,我们看看ScrollView中对于滑动事件是如何做处理的:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        /*
        * 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;
        }
 
        /*...*/
}

解决思路:

1.内部获取滑动控制权:
那么我想问你一下电脑前的各位,发现了原因后你们知道怎么解决了吗?其实很简单,控制外层不要让它拦截属于内层的滑动就好了,那么怎么做呢,我发现在ScrollView的onInterceptTouchEvent()中并没有对用户手指摁下的动作进行拦截,也就是没有对MotionEvent.ACTION_DOWN进行拦截,这就意味着当用户手指摁下时,这个动作是可以传递到ScrollVIew内部中其他VIew上的(不然ScrollView中其他View的点击长摁事件怎么响应),那么我们就可以重写一个ScrollView,当它接收到MotionEvent.ACTION_DOWN这个动作时请求外层不对动作进行拦截。这点很重要,因为这时内部的ScrollVIew就掌握了主动权,可以处理用户手指在屏幕上的一系列操作

2.内外层滑动关联:
那么,到这里就解决完了吗?不不不,这只是开头,如果此时你跟着我上面的思路敲完了代码,你会发现内外两个ScrollVIew的滑动事件是独立的,虽然内部的ScrollView可以滑动,但是当它滑动到顶部或底部时并没有跟外部的ScrollView进行联动,这样用户的滑动体验会很差,怎么解决呢?发动你机智的小脑瓜想一想,对咯,那就是在合适的时机将外层的拦截权还给它。那什么是合适的时机呢?无非就是内部ScrollView滑动到顶或者底的时候。
OK,问题都清楚了,方案也提供了,现在你就可以试着写写代码尝试解决冲突了

实际代码:

这里我把我写的代码放出来,供大家参考:

public class MyScrollView extends ScrollView {
    
    public MyScrollView(Context context) {
        super(context);
    }
 
    public MyScrollView(Context context) {
        super(context);
    }
 
    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 
     @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        maxY = getChildAt(0).getMeasuredHeight() - getMeasuredHeight();
    }
 
    //在Y轴上可以滑动的最大距离 = 总长度 - 当前展示区域长度
    private int maxY;
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (getScrollY() > 0 && getScrollY() < maxY)
                    getParent().requestDisallowInterceptTouchEvent(true);
                else
                    getParent().requestDisallowInterceptTouchEvent(false);
                /*if (getScrollY()==0)
                    Log.i("kkk","滑到头了");
                if (getChildAt(0).getMeasuredHeight() == getScrollY() + getHeight())
                    Log.i("kkk","滑到底了");*/
                break;
            case MotionEvent.ACTION_UP:
                getParent().getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
}

布局结构:


 
    
 
        
 
        
 
        
 
        
 
            
 
                
 
                
 
                
 
                
 
                
 
                
            
        
 
       
        
 
        
 
        
 
        
 
       
        
 
        
 
        
    


总结:

在我最开始接触Android开发的时候,对这种事件冲突的问题是一点办法都没有,因为不知道怎么排查问题,也不知道如何下手,不过随着对Android知识的不断加深,反过头来在看这些问题也不过如此,人生又何尝不是这样呢,不管过去还是未来,总是有着我们不懂的问题在等着我们去解决,而我们则在这个解决的过程中不断的进步和成长,成就更好的自己,没有谁的学习过程是一帆风顺的 每一个人都是一步步爬着过来的,爱万难,迎万难;

你可能感兴趣的:(完美解决Android中的ScrollView嵌套ScrollView滑动冲突问题)