Android: Scrolling issues with recyclerview inside

2019-05-01 16:44发布

问题:

I have a RecyclerView inside a ViewPager that only occupies the bottom half of the screen, and what I want to do is have the entire screen scroll if the RecyclerViews received a vertical scroll event.

UI hierarchy in a nutshell:

CoordinatorLayout 
   --- AppBarLayout
       --- Toolbar
       --- Bunch of static LinearLayouts, occupy most of screen
       --- TabLayout
   --- ViewPager
       --- Fragments with RecyclerView

What I have:

To my understanding, a RecyclerView is memory-efficient and tries to fit itself in whatever space is available in the screen. In my app, I have a ViewPager hosting multiple tabs, each of which have a RecyclerView to display different things.

Could you please give me some ideas of what I could do to make the whole screen scroll? I'm guessing the best shot is to add a CollapsingToolbarLayout with parallax to the static content in the middle of the screen. I even tried to do this, but the UI was completely broken. It's difficult since I only want the content in the middle of the screen to scroll out, not the toolbars on top. I didn't have much luck with a NestedScrollView either, apparently its compatibility with ViewPager and CoordinatorLayout is not straight-forward..

What I want:

Main Activity Layout:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        >

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar_contributor"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:focusableInTouchMode="true">

            <android.support.v7.widget.SearchView
                android:id="@+id/searchview_contributor"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:icon="@drawable/ic_search_white_24dp"
                app:defaultQueryHint="Bla bla"
                app:iconifiedByDefault="false"/>

            <ImageButton
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:background="@android:color/transparent"
               android:src="@drawable/ic_person_outline_black_24dp"/>

        </android.support.v7.widget.Toolbar>

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

            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight=".5"
                android:text="La Bla"/>

            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight=".5"
                android:text="Bla Bla"/>
        </LinearLayout>

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

            <!-- Bunch of stuff, but irrelevant -->

            <android.support.design.widget.TabLayout
                android:id="@+id/tablayout_profile"
                android:layout_width="match_parent"
                android:layout_height="40dp"/>
        </LinearLayout>

        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager_profile"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

The fragment inside the Viewpager:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview_photos"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

Thanks in advance!

Update: The responses I received here (for which I am thankful) came a bit too late. If anyone knows which is the correct answer, please let me know!

回答1:

public class CustomRecyclerView extends RecyclerView {
public CustomRecyclerView(Context context) {
    super(context);
}

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

public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

int lastEvent = -1;

boolean isLastEventIntercepted = false;
private float xDistance, yDistance, lastX, lastY;

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

            break;

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

            if (isLastEventIntercepted && lastEvent == MotionEvent.ACTION_MOVE) {
                return false;
            }

            if (xDistance > yDistance) {

                isLastEventIntercepted = true;
                lastEvent = MotionEvent.ACTION_MOVE;
                return false;
            }
    }

    lastEvent = e.getAction();

    isLastEventIntercepted = false;
    return super.onInterceptTouchEvent(e);
}

}

Just change your recyclerview by this .



回答2:

I've done something very similar to this with a CollapsingToolbarLayout.

The trick here is to have two toolbars, one at the top with the searchview and another that is the toolbar dedicated to the collapsing layout.

Your searchview toolbar will have app:collapseMode="pin".

Your button bar will have app:collapseMode="pin".

Your LinearLayout below the button bar will have the default collapse mode so it will scroll. You can even give it a parallax effect with app:collapseMode="parallax".

Your TabLayout will have app:collapseMode="pin" so it scrolls up under the button bar and pins there.

Your ViewPager should be outside the CollapsingToolbarLayout and have the attribute app:layout_behavior="@string/appbar_scrolling_view_behavior".

The CollapsingToolbarLayout will want to display a title. Just call setTitle(null) on the collapsing toolbar and set your real title on the searchview toolbar.

You can put your navigation and menus on either the searchview toolbar and the collapsing toolbar and they should work as expected.

I will post some XML later; I just wanted to get this answer started.



回答3:

It looks like you simply miss the app:layout_scrollFlags attribute. You should add it both to the Toolbar and your linear header layouts. For example, you could use: app:layout_scrollFlags="scroll|enterAlways"

As I see, your ViewPager already has app:layout_behavior="@string/appbar_scrolling_view_behavior", sou it should work right after you specify the scrollFlags