NestedScrollView and Horizontal RecyclerView Smooth Scrolling

后端 未结 3 1508
后悔当初
后悔当初 2020-12-08 05:00

I have a single vertical nestedscrollview that contains a bunch of recyclerview with a horizontal layoutmanager setup. The idea is pretty similar to how the new google play

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-08 05:54

    Since falc0nit3 solution doesn't work anymore (currently the project using 28.0.0 version of support library), i have found an another one.

    The background reason of the issue is still the same, scrollable view eats on down event by returning true on the second tap, where it shouldn't, because naturally second tap on the fling view stops scrolling and may be used with next move event to start opposite scroll The issue is reproduced as with NestedScrollView as with RecyclerView. My solution is to stop scrolling manually before native view will be able to intercept it in onInterceptTouchEvent. In this case it won't eat the ACTION_DOWN event, because it have been stopped already.

    So, for NestedScrollView:

    class NestedScrollViewFixed(context: Context, attrs: AttributeSet) :
        NestedScrollView(context, attrs) {
    
        override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
            if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
                onTouchEvent(ev)
            }
            return super.onInterceptTouchEvent(ev)
        }
    }
    

    For RecyclerView:

    class RecyclerViewFixed(context: Context, attrs: AttributeSet) :
        RecyclerView(context, attrs) {
    
        override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
            if (e.actionMasked == MotionEvent.ACTION_DOWN) {
                this.stopScroll()
            }
            return super.onInterceptTouchEvent(e)
        }
    
    }
    

    Despite solution for RecyclerView looks easy to read, for NestedScrollView it's a bit complicated. Unfortunately, there is no clear way to stop scrolling manually in widget, which the only responsibility is to manage scroll (omg). I'm interesting in abortAnimatedScroll() method, but it is private. It is possible to use reflection to get around it, but for me better is to call method, which calls abortAnimatedScroll() itself. Look at onTouchEvent handling of ACTION_DOWN:

     /*
     * If being flinged and user touches, stop the fling. isFinished
     * will be false if being flinged.
     */
    if (!mScroller.isFinished()) {
        Log.i(TAG, "abort animated scroll");
        abortAnimatedScroll();
    }
    

    Basically stopping fling is managed in this method, but a bit later, than we have to call it to fix the bug

    Unfortunately due to this we can't just create OnTouchListener and set it outside, so only inheritance fits the requirements

提交回复
热议问题