记录NestedScrollView的一个小坑

记录一个NestedScrollView的小坑


  需求是这样的要实现上面一个banner,下面是一个TabLayout+Fragment+RecyclerView的布局,然后要求页面上滑时Tab要吸顶,然后列表滚动。这应该是很常见的一个布局了。看到这个需求自然第一时间就想到了NestedScrollView,只需要重写onNestedPreScroll,然后在里面做一下判断,当向下滑动距离小于顶部Banner高度时,父布局消费掉滑动;当向下滑动距离大于Banner高度时由RecyclerView来消费滑动事件。

@Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        boolean headerScrollUp = dy > 0 && getScrollY() < mNestedScrollHeight;
        boolean headerScrollDown = dy < 0 && getScrollY() > 0 && !target.canScrollVertically(-1);
        if (headerScrollUp || headerScrollDown) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
        super.onNestedPreScroll(target, dx, dy, consumed, type);
    }

  代码类似上面这样,mNestedScrollHeight是Banner的高度。然后写好代码Run一下,嗯?没反应?不生效?what fuck?是我的逻辑不对吗?不可能啊?不禁陷入了程序猿的沉思。
DEBUG一下吧。在onNestedPreScroll里面打个断点,发现根本没执行进来!这就很奇怪了,又查一查网上的大家的解决方案,大家不都是这么做的吗?于是又陷入了自我怀疑。
  折腾了一下午,还是没折腾出来,期间还用其他方案来做嵌套滑动,效果都实现差不多了,就是实现方式非常的ugly。秉承着一个稍微有点代码洁癖的程序猿的自觉,我还是决定搞清楚为啥onNestedPreScroll没有被调用。所以终极解决方案就是,read the fucking source code!
  由于这里嵌套滑动的子布局是RecyclerView,所以消费滑动的事件是由RecyclerView发出的,然后在RecyclerView的onTouchEvent,在ACTION_DOWN中发出的startNestedScroll,具体实现是在NestedScrollingChildHelper。通过再次DEBUG,我确定了RecyclerView的ViewParent确实是我自定义的StickyNestedScrollView(继承自NestedScrollView),吃了个定心丸。然后回到RecyclerView的onTouchEvent,在ACTION_MOVE中调用了dispatchNestedPreScroll,向嵌套父布局发起嵌套滑动的事件,交给父布局先处理。dispatchNestedPreScroll的具体实现也是在NestedScrollingChildHelper,然后在里面调用了ViewParentCompat.onNestedPreScroll,看一下ViewParentCompat.onNestedPreScroll的源码。

    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy, int[] consumed, int type) {
        if (parent instanceof NestedScrollingParent2) {
            ((NestedScrollingParent2)parent).onNestedPreScroll(target, dx, dy, consumed, type);
        } else if (type == 0) {
            if (VERSION.SDK_INT >= 21) {
                try {
                    parent.onNestedPreScroll(target, dx, dy, consumed);
                } catch (AbstractMethodError var7) {
                    Log.e("ViewParentCompat", "ViewParent " + parent + " does not implement interface " + "method onNestedPreScroll", var7);
                }
            } else if (parent instanceof NestedScrollingParent) {
                ((NestedScrollingParent)parent).onNestedPreScroll(target, dx, dy, consumed);
            }
        }

    }

NestedScrollView实现的是NestedScrollingParent2,所以这里调用了onNestedPreScroll(target, dx, dy, consumed, type),看到这里,就发现,这个方法怎么和我重写的方法不太一样啊?
  然后继续看了一下继承关系,NestedScrollingParent2接口继承自NestedScrollingParent接口,然后增加了一同名的函数onNestedPreScroll,但是增加了一个参数;NestedScrollView实现了NestedScrollingParent2接口。而最终调用的是NestedScrollingParent2的onNestedPreScroll(target, dx, dy, consumed, type)方法。所以,我们需要继承NestedScrollView,重写NestedScrollingParent2的onNestedPreScroll(target, dx, dy, consumed, type)方法,在里面做处理才可以。就因为这个同名方法,可坑死我了,以后遇到类似的事情可得长点心了。

你可能感兴趣的:(android学习)