How to avoid blocking of scrolling itself when using setNestedScrollingEnabled(false)?

后端 未结 8 1869
盖世英雄少女心
盖世英雄少女心 2020-12-30 01:07

Background

We have quite a complex layout that has CollapsingToolbarLayout in it, together with a RecyclerView at the bottom.

In certain cases, we tempora

8条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-30 01:47

    I believe that this problem is related to the collapsing toolbar snapping into place (either closed or open) and leaving a vertical offset variable (mScrollOffset[1] in RecyclerView) with a non-zero value that subsequently biases the scroll - slowing or reversing the scroll in one direction and speeding it up in the other. This variable only seems to be set in NestedScrollingChildHelper if nested scrolling is enabled. So, whatever value mScrollOffset[1] has goes unchanged once nest scrolling is disabled.

    To reliably reproduce this issue, you can cause the toolbar to snap into place then immediately click disable. See this video for a demonstration. I believe, that the magnitude of the issue varies by how much "snapping" occurs.

    If I drag the toolbar to the fully open or closed position and don't let it "snap", then I have not been able to reproduce this problem and mScrollOffset[1] is set to zero which I think is the right value. I have also reproduced the problem by removing snap from the layout_scrollFlags of the collapsing toolbar in the layout and placing the toolbar in a partially open state.

    If you want to play around with this, you can put your demo app into debug mode and observe the value of mScrollOffset[1] in RecyclerView#onTouchEvent. Also take a look at NestedScrollingChildHelper's dispatchNestedScroll and dispatchNestedPreScroll methods to see how the offset is set only when nested scrolling is enabled.

    So, how to fix this? mScrollOffset is private toRecyclerView and it is not immediately obvious how to subclass anything to change the value of mScrollOffset[1]. That would leave Reflection, but that may not be desirable to you. Maybe another reader has an idea about how to approach this or knows of some secret sauce. I will repost if anything occurs to me.

    Edit: I have provided a new ScrollingActivity.java class that overcomes this issue. It does use reflection and applies a patch to set mScrollOffset[1] of RecyclerView to zero when the disable scroll button has been pressed and the AppBar is idle. I have done some preliminary testing and it is working. Here is the gist. (See updated gist below.)

    Second edit: I was able to get the toolbar to snap in funny ways and get stuck in the middle without the patch, so it doesn't look like the patch is causing that particular issue. I can get the toolbar to bounce from fully open to collapsed by scrolling down fast enough in the unpatched app.

    I also took another look at what the patch is doing and I think that it will behave itself: The variable is private and referred to only in one place after scrolling is turned off. With scrolling enabled, the variable is always reset before use. The real answer is for Google to fix this problem. Until they do, I think this may be the closest you can get to an acceptable work-around with this particular design. (I have posted an updated gist that addresses potential issues with a quick click-around leaving switches in a potential unsuitable state.)

    Regardless, the underlying issue has been identified and you have a reliable way to reproduce the problem, so you can more easily verify other proposed solutions.

    I hope this helps.

提交回复
热议问题