ScrollView 嵌套 RecyclerVeiw, 輕松解決滑動沖突

過場背景

滑動沖突

在開發(fā)android中, 滑動沖突是一常見的事件沖突。
列如:在scrollView中嵌套listView 或 recyclerView。
由于這2種視圖都可以滑動,就會導致父視圖攔截了滑動事件,從而導致子視圖獲取不到滑動事件。

如何解決

第一種: 清晰的了解android的事件分發(fā)機制,在各個view的攔截事件中做相應的處理。

第二種:android在Lollipop之后為滑動機制提供了NestedScrolling特性,可以使用NestedScrollingChild和NestedScrollingParent 來解決滑動沖突。

今天我們主要來講第二種實現(xiàn)方式。

我們先看下他們的方法對應的關系:

子view 父view
startNestedScroll onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

一般是子View發(fā)起,父view接受回調(diào)。

首先,我們先了解做為子view的RecyclerView是如何實現(xiàn)和調(diào)用NestedScrollingChild接口中的方法。

我們來看看reyclerView的onTouch()方法:

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
 .......
    @Override
    public boolean onTouchEvent(MotionEvent e) {
      .......
        
        if (action == MotionEvent.ACTION_DOWN) {
            mNestedOffsets[0] = mNestedOffsets[1] = 0;
        }
        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
        
        switch (action) {
            .......
            case MotionEvent.ACTION_MOVE: {
                .......
                int dx = mLastTouchX - x;
                int dy = mLastTouchY - y;

                //在recyclerView進行滑動之前,會先調(diào)用dispatchNestedPreScroll來判斷父view是否需要處理
                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
                    // 這里主要對mScrollOffset數(shù)組賦值, mScrollConsumed[0]代表父View 在x坐標消耗的滑動
                    //的數(shù)值,mScrollConsumed[1]表示父View在Y軸消耗的滑動數(shù)值
                    dx -= mScrollConsumed[0];
                    dy -= mScrollConsumed[1];
                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                    // Updated the nested offsets
                    mNestedOffsets[0] += mScrollOffset[0];
                    mNestedOffsets[1] += mScrollOffset[1];
                }
    
                .......
        }
        .......
        
        return true;
    }
}
 .......

我們發(fā)現(xiàn)recyclerView在滑動之前會調(diào)用dispatchNestedPreScroll方法來判斷是否有實現(xiàn)NestedScrollingParent 的父View處理。有的話就會執(zhí)行里方法,減去父View已經(jīng)消耗的滑動值。

OK 現(xiàn)在我們了解這些,可以正式進入我們的主題了。

如何快速的解決在scrollView中嵌套RecyclerVeiw的事件沖突?

我們可以看到,recylcerView已經(jīng)實現(xiàn)了NestedScrollingChild 接口, 所以我們只需要自定義scrollView。我們只要讓scrollVeiw實現(xiàn)NestedScrollingParent,在recyclerView發(fā)起調(diào)用時做相應的操作就可以了。

看下我是如何實現(xiàn)的:

package eebochina.com.testtechniques.nestedScroll;

import android.content.Context;
import android.support.v4.view.NestedScrollingParent;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;

/**
 * Created by User on 2017/1/17.
 */
public class NestedParent extends ScrollView implements NestedScrollingParent {

    //方便測試先固定。
    private int maxHeight = 464;
    private RecyclerView mRecyclerView;

    public NestedParent(Context context) {
        super(context);
    }

    public NestedParent(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NestedParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    public void setMaxHeight(int maxHeight) {
        this.maxHeight = maxHeight;
    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(child, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        super.onNestedScrollAccepted(child, target, axes);
    }

    @Override
    public void onStopNestedScroll(View target) {
        super.onStopNestedScroll(target);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        //這里可以不處理, 因為srollView內(nèi)部已經(jīng)重寫了改方法,我們可以直接調(diào)用
        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
         return super.onNestedFling(target, velocityX, velocityY, consumed);;
    }

    //返回true代表父view消耗滑動速度,子View將不會滑動
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        if (null == mRecyclerView) mRecyclerView = (RecyclerView) target;
        if (mRecyclerView.computeVerticalScrollOffset() != 0) {
            return false;
        }
        this.fling((int) velocityY);
        return true;
    }

    //對應子view 的dispatchNestedPreScroll方法, 最后一個數(shù)組代表消耗的滾動量,下標0代表x軸,下標1代表y軸
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        //判斷是否滾動到最大值
        if ( dy >= 0 && this.getScrollY() < maxHeight) {
            if (null == mRecyclerView) mRecyclerView = (RecyclerView) target;
            //計算RecyclerView的偏移量, 等于0的時候說明recyclerView沒有滑動,否則應該交給recyclerView自己處理
            if (mRecyclerView.computeVerticalScrollOffset() != 0) return;
            this.smoothScrollBy(dx, dy);
            consumed[1] = dy; //consumed[1]賦值為 dy ,代表父類已經(jīng)消耗了改滾動。
        }
    }
}

代碼非常少,主要是實現(xiàn)了NestedScrollingParent方法,并在滑動和滾動的方法進行處理。
主要邏輯也進行了相應的注釋,最大值為了方便測試現(xiàn)也寫了一個固定值,也預留了賦值方法。
我們看下效果圖:


nestedScroll.gif

完整代碼地址:
https://github.com/hu5080126/SimpleExample/tree/master/nestedScrollView/src

到這里已經(jīng)寫完了, 希望可以幫到大家。 ( 如果能去了解事件的分發(fā)機制那肯定是最好的)
大家有什么好的建議,歡迎提出。
(只支持5.0+的版本)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,234評論 25 708
  • android在嵌套滑動的時候會產(chǎn)生滑動沖突。之前我也碰到,但是以前的筆記本丟失了,所以只能重新再寫一章。 一.會...
    鍵盤上的麒麟臂閱讀 6,412評論 1 13
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 47,170評論 22 665
  • 很多父母都希望自己的子女聽話,乖巧,可有沒有想過這或許是一種危害,說明你的子女還沒有成熟起來。 初中的學生大多屬于...
    清茶與酒_3cde閱讀 575評論 0 0
  • 今天下午看著手機PDF格式的書做筆記,掏出包內(nèi)的兩本筆記本,赫然發(fā)現(xiàn),竟然都是寫了一半的筆記本,發(fā)現(xiàn)這個事兒,我立...
    將軍_84fd閱讀 634評論 0 0

友情鏈接更多精彩內(nèi)容