How to put RecyclerView inside NestedScrollView?

后端 未结 4 1509
南方客
南方客 2020-11-30 18:30

With creation of NestedScrollView you can put scrolling view inside another scrolling view as long as those implement NestedScrollingChild and NestedScrollingParent correctl

相关标签:
4条回答
  • 2020-11-30 18:49

    I have wasted at least a week behind this and the only solution that works is to remove the nestedscrollview. I know that is not the answer to this particular question of adding recyclerview inside nestedscrollview, but to make the recyclerview to actually recycle the view, you HAVE TO REMOVE the nestedscrollview. If you happen to have multiple view inside instead of just the recyclerview, You have to add them as items of the recyclerview by setting different viewholders, and then adding

    app:layout_behavior="@string/appbar_scrolling_view_behavior"

    to the recyclerview or the parent of recyclerview(the parent cannot have any other view other than Recyclerview for this to work).

    In my case, the requirement was as follows:

      <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:fitsSystemWindows="true"
            app:layout_constraintTop_toTopOf="parent">
    
            <!-- Scrollable view here -->
    
            <com.google.android.material.appbar.AppBarLayout
                ....   >
    
                <com.google.android.material.appbar.CollapsingToolbarLayout
                    ....>
    
                    <androidx.appcompat.widget.Toolbar
                        ....>
    
                        <include
                            android:id="@+id/toolbar_header_view"
                            .... />
                    </androidx.appcompat.widget.Toolbar>
    
                    <include layout="@layout/widget_header" />
                </com.google.android.material.appbar.CollapsingToolbarLayout>
            </com.google.android.material.appbar.AppBarLayout>
    
            <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
                ....>
                <LinearLayout orientation="vertical" ....>
                    <View .../>  <!-- I needed to have these two views along with recyclerview and i wanted them to do nestedscroll along with recyclerview -->
                    <View .../>
                    <androidx.recyclerview.widget.RecyclerView
                    .... />
            </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    I needed to have two views before recyclerview and I wanted them to do nestedscroll along with recyclerview. The only thing that worked was by removing these to views and adding them as the first two items of recyclerview, and then adding

    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    

    to the swipetorefresh layout

    Hope this helps someone. I will add more info If required.

    0 讨论(0)
  • 2020-11-30 18:51

    The following is my new updated answer:

    <android.support.v4.widget.NestedScrollView
            android:id="@+id/scrollview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <android.support.v7.widget.CardView
                    android:id="@+id/cardview1"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/card_margin">
    
                    <LinearLayout
                        style="@style/Widget.CardContent"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
    
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:text="Info CardView1"
                            android:textAppearance="@style/TextAppearance.AppCompat.Title" />
    
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:text="@string/cheese_ipsum" />
    
                    </LinearLayout>
    
                </android.support.v7.widget.CardView>
    
                <android.support.v7.widget.CardView
                    android:id="@+id/cardview2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/cardview1"
                    android:layout_margin="@dimen/card_margin">
    
                    <LinearLayout
                        style="@style/Widget.CardContent"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
    
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:text="Info CardView2"
                            android:textAppearance="@style/TextAppearance.AppCompat.Title" />
    
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:text="@string/cheese_ipsum" />
    
                    </LinearLayout>
    
                </android.support.v7.widget.CardView>
    
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerview"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/cardview2"
                    android:clipToPadding="false"
                    android:paddingTop="0dp"/>
    
            </RelativeLayout>
    
        </android.support.v4.widget.NestedScrollView>
    

    In Activity:

            RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(true); // true: with header
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);            
            final MyLinearLayoutManager layoutManager = new MyLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false, getScreenHeight(this));
            // final CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
            recyclerView.setLayoutManager(layoutManager);
            recyclerView.setAdapter(recyclerViewAdapter);  
            // recyclerView.setNestedScrollingEnabled(false); // Disables scrolling for RecyclerView, however, CustomLinearLayoutManager used instead of MyLinearLayoutManager
    

    I have also updated to My GitHub's sample project

    Screenshot:


    0 讨论(0)
  • 2020-11-30 18:52

    Here is solution to call server only when you are realy need to load more data. In this way you can put your endless RecyclerView and many other views inside NestedScrollView. For me it is working well.

    1. Create EndlessParentScrollListener class to handle scroll events from NestedSrollView.

    public abstract class EndlessParentScrollListener implements NestedScrollView.OnScrollChangeListener {
            // The current offset index of data you have loaded
            private int currentPage = 0;
            // The total number of items in the dataset after the last load
            private int previousTotalItemCount = 0;
            // True if we are still waiting for the last set of data to load.
            private boolean loading = true;
            // Sets the starting page index
            private int startingPageIndex = 0;
            // The minimum amount of pixels to have below your current scroll position
            // before loading more.
            private int visibleThresholdDistance = 300;
    
            RecyclerView.LayoutManager mLayoutManager;
    
            public EndlessParentScrollListener(RecyclerView.LayoutManager layoutManager) {
                this.mLayoutManager = layoutManager;
            }
    
            @Override
            public void onScrollChange(NestedScrollView scrollView, int x, int y, int oldx, int oldy) {
                // We take the last son in the scrollview
                View view = scrollView.getChildAt(scrollView.getChildCount() - 1);
                int distanceToEnd = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
    
                int totalItemCount = mLayoutManager.getItemCount();
                // If the total item count is zero and the previous isn't, assume the
                // list is invalidated and should be reset back to initial state
                if (totalItemCount < previousTotalItemCount) {
                    this.currentPage = this.startingPageIndex;
                    this.previousTotalItemCount = totalItemCount;
                    if (totalItemCount == 0) {
                        this.loading = true;
                    }
                }
    
                // If it’s still loading, we check to see if the dataset count has
                // changed, if so we conclude it has finished loading and update the current page
                // number and total item count.
                if (loading && (totalItemCount > previousTotalItemCount)) {
                    loading = false;
                    previousTotalItemCount = totalItemCount;
                }
    
                // If it isn’t currently loading, we check to see if we have breached
                // the visibleThreshold and need to reload more data.
                // If we do need to reload some more data, we execute onLoadMore to fetch the data.
                // threshold should reflect how many total columns there are too
                if (!loading && distanceToEnd <= visibleThresholdDistance) {
                    currentPage++;
                    onLoadMore(currentPage, totalItemCount);
                    loading = true;
                }
            }
    
            // Defines the process for actually loading more data based on page
            public abstract void onLoadMore(int page, int totalItemsCount);
        }
    

    2. Set listener

    private void initRecycler() {  
            //TODO init recycler adapter here
    
            recycler.setNestedScrollingEnabled(false);          
            LinearLayoutManager _layoutManager = new LinearLayoutManager(this);
            recycler.setLayoutManager(_layoutManager);
            NestedScrollView scrollView = (NestedScrollView) findViewById(R.id.scrollView);
            scrollView.setOnScrollChangeListener(new EndlessParentScrollListener(_layoutManager) {
                    @Override
                    public void onLoadMore(int page, int totalItemsCount) {                     
                        if (loadedItemCount < serverItemsCount)
                            customLoadMoreDataFromApi();
                    }
                });
            customLoadMoreDataFromApi();
        }
    

    Short xml if someone find it usefull:

    <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:background="@android:color/background_light"
            android:fitsSystemWindows="true">
    
    
            <android.support.design.widget.AppBarLayout>
                ...
            </android.support.design.widget.AppBarLayout>
    
            <android.support.v4.widget.NestedScrollView
                android:id="@+id/scrollView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scrollbars="vertical"
                android:scrollbarAlwaysDrawVerticalTrack="true"
                app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:scrollbarAlwaysDrawVerticalTrack="false"
                    android:scrollbars="vertical">
    
                    <!-- some views goes here-->
    
                    <android.support.v7.widget.RecyclerView
                        android:id="@+id/recyclerFeed"
                        android:scrollbars="vertical"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    
                    <!-- and possibly here-->
    
                </LinearLayout>
            </android.support.v4.widget.NestedScrollView>
    
        </android.support.design.widget.CoordinatorLayout>
    
    0 讨论(0)
  • 2020-11-30 19:04

    So put RecyclerView inside NestedScrollView directly will unfortunately display nothing. However, there is a way to put the recyclerview inside the NestedScrollView indirectly - just use a frameLayout as the third party to hold your recyclerview.

    This is the framelayout which holds the nested recyclerview in your activity class:

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            tools:context=".ExampleFragment"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <android.support.v4.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fillViewport="true"
                >
            </android.support.v4.widget.NestedScrollView>
       </FrameLayout>
    

    Put your fragment into the framelayout (code sits within the activity class):

     ExampleFragment exampleFragment = new ExampleFragment();
    
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.container, exampleFragment);
        ft.commit();
    

    In your exampleFragment, you can then put your recyclerview.

    <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/post_container"
            android:background="#E0E0E0">
    
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/my_recycler_view"
                    android:scrollbars="vertical"
    
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
                </android.support.v7.widget.RecyclerView>
    
    
        </RelativeLayout>
    

    This is the fragment code:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    
        super.onCreateView(inflater, container, savedInstanceState);
                llLayout = (RelativeLayout) inflater.inflate(R.layout.example_fragment_layout, container, false);
                        mRecyclerView = (RecyclerView) llLayout.findViewById(R.id.my_recycler_view);
    

    The following is the CheeseSquare XML layout you should have:

    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/detail_backdrop_height"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            android:fitsSystemWindows="true">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorPrimary"
                app:expandedTitleMarginStart="48dp"
                app:expandedTitleMarginEnd="64dp">
    
                <ImageView
                    android:id="@+id/backdrop"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:fitsSystemWindows="true"
                    app:layout_collapseMode="parallax" />
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                    app:layout_collapseMode="pin" />
    
            </android.support.design.widget.CollapsingToolbarLayout>
    
        </android.support.design.widget.AppBarLayout>
    
       <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            tools:context=".ExampleFragment"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <android.support.v4.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fillViewport="true"
                >
            </android.support.v4.widget.NestedScrollView>
       </FrameLayout>
    
        <android.support.design.widget.FloatingActionButton
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            app:layout_anchor="@id/appbar"
            app:layout_anchorGravity="bottom|right|end"
            android:src="@drawable/ic_discuss"
            android:layout_margin="@dimen/fab_margin"
            android:clickable="true"/>
    
    </android.support.design.widget.CoordinatorLayout>
    
    0 讨论(0)
提交回复
热议问题