How to place a RecyclerView inside a NestedScrollView?

With the creation of NestedScrollView, you can put the scroll in another scrollable view if they implement NestedScrollingChild and NestedScrollingParent correctly.

(This is a good Yan Lake design template (from Google), actually recommends putting RecyclerView inside the subview: plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T ")

I want to put a RecyclerView inside a NestedScrollView and, fortunately, RecyclerView implements a NestedScrollingChild , so you can put it inside a NestedScrollView .

 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild 

I read these posts:

How to use RecyclerView inside NestedScrollView?

NestedScrolling with NestedScrollView, RecyclerView (horizontal) inside the coordinator Layout

But the problem with most of the voted solutions is that it calls all RecyclerView elements, for example, if it is an infinite RecyclerView, and when the user reaches the end of the list that you want to make network, then with this solution RecyclerView calls the server again, because it automatically reaches the last element of the RecyclerView .

Anyway, how to set the parameter so that I can put the RecyclerView inside the NestedScrollView . (actually I want to set the view group as framelayout or relativelayout as one child of the nestedscrollview, and then I want to put the recyclerview inside the framelayout or relativelayout)

When I put the RecyclerView inside the NestedScrollView , nothing is visible.




To create a sample project, you can use cheesesquare and change the CheeseDetailActivity to have a RecyclerView.




Although BNK's answer is incorrect, BNK has tried a lot. Therefore, I reward him with generosity. Still looking for a good solution ....

+45
android android-recyclerview
25 Oct '15 at 13:46
source share
3 answers

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 action:

  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 also upgraded to an example of my github project

Screenshot:

enter image description here




+29
Nov 03 '15 at 9:00
source share

Here is the solution for calling the server only when you really need to load more data. This way you can place an infinite RecyclerView and many other views inside a NestedScrollView. It works well for me.

1. Create an 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. Install a 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 finds it useful:

 <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> 
+15
Jul 25 '16 at 14:12
source share

Thus, the RetyclerView inside the NestedScrollView will, unfortunately, show nothing. However, there is a way to place the recyclerview inside the NestedScrollView indirectly - just use frameLayout as a third party to store your recyclerview.

This is a frameelayout that contains a 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 the snippet in framelayout (the code is in the activity class):

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

In exampleFragment, you can reinstall it.

 <?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 a snippet :

 @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); 

Below 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> 
+9
Nov 02 '15 at 21:16
source share



All Articles