Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
317 views
in Technique[技术] by (71.8m points)

android - NestedScrollView and Horizontal RecyclerView Smooth Scrolling

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 store looks. I'm able to make it functional but it isn't smooth at all. Here are the problems:

1) The horizontal recyclerview item fails to intercept the touch event most of the times even though i tap right on it. The scroll view seems to take precedence for most of the motions. It's hard for me to get a hook onto the horizontal motion. This UX is frustrating as I need to try a few times before it works. If you check the play store, it is able to intercept the touch event really well and it just works well. I noticed in the play store the way they set it up is many horizontal recyclerviews inside one vertical recyclerview. No scrollview.

2) The height of the horizontal recyclerviews have to be manually set and there is no easy way to calculate the height of the children elements.

Here is the layout I'm using:

<android.support.v4.widget.NestedScrollView
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:background="@color/dark_bgd"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/main_content_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"
            tools:visibility="gone"
            android:orientation="vertical">                

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/starring_list"
                    android:paddingLeft="@dimen/spacing_major"
                    android:paddingRight="@dimen/spacing_major"
                    android:layout_width="match_parent"
                    android:layout_height="180dp" />

This UI pattern is very basic and most likely used in many different apps. I've read many SO's where ppl say it's a bad idea to put a list within a list, but it is a very common and modern UI pattern used all over the place.Think of netflix like interface with a series of horizontal scroll lists inside a vertical list. Isn't there a smooth way to accomplish this?

Example image from the store:

Google Play Store

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So the smooth scrolling issue is fixed now. It was caused by a bug in the NestedScrollView in the Design Support Library (currently 23.1.1).

You can read about the issue and the simple fix here: https://code.google.com/p/android/issues/detail?id=194398

In short, after you performed a fling, the nestedscrollview didn't register a complete on the scroller component and so it needed an additional 'ACTION_DOWN' event to release the parent nestedscrollview from intercepting(eating up) the subsequent events. So what happened was if you tried scrolling your child list(or viewpager), after a fling, the first touch releases the parent NSV bind and the subsequent touches would work. That was making the UX really bad.

Essentially need to add this line on the ACTION_DOWN event of the NSV:

computeScroll();

Here is what I'm using:

public class MyNestedScrollView extends NestedScrollView {
private int slop;
private float mInitialMotionX;
private float mInitialMotionY;

public MyNestedScrollView(Context context) {
    super(context);
    init(context);
}

private void init(Context context) {
    ViewConfiguration config = ViewConfiguration.get(context);
    slop = config.getScaledEdgeSlop();
}

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

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


private float xDistance, yDistance, lastX, lastY;

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    final float x = ev.getX();
    final float y = ev.getY();
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            xDistance = yDistance = 0f;
            lastX = ev.getX();
            lastY = ev.getY();

            // This is very important line that fixes 
           computeScroll();


            break;
        case MotionEvent.ACTION_MOVE:
            final float curX = ev.getX();
            final float curY = ev.getY();
            xDistance += Math.abs(curX - lastX);
            yDistance += Math.abs(curY - lastY);
            lastX = curX;
            lastY = curY;

            if (xDistance > yDistance) {
                return false;
            }
    }


    return super.onInterceptTouchEvent(ev);
}

}

Use this class in place of your nestedscrollview in the xml file, and the child lists should intercept and handle the touch events properly.

Phew, there are actually quite a few bugs like these that makes me want to ditch the design support library altogether and revisit it when its more mature.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...