How to use RecyclerView inside NestedScrollView?
AndroidAndroid RecyclerviewAndroid Problem Overview
How to use RecyclerView
inside NestedScrollView
?
RecyclerView
content is not visible after setting adapter.
UPDATE layout code updated.
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/keyline_1">
</RelativeLayout>
<View
android:id="@+id/separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e5e5e5" />
<android.support.v7.widget.RecyclerView
android:id="@+id/conversation"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Android Solutions
Solution 1 - Android
Replace your recyclerView with,
<android.support.v7.widget.RecyclerView
android:id="@+id/conversation"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
here,
app:layout_behavior="@string/appbar_scrolling_view_behavior"
will manage the rest of things.
One more thing, no need to put your recyclerView inside NestedScrollView
Solution 2 - Android
UPDATE 1
Since Android Support Library 23.2.0 there were added method setAutoMeasureEnabled(true)
for LayoutManagers. It makes RecyclerView to wrap it's content and works like a charm.
http://android-developers.blogspot.ru/2016/02/android-support-library-232.html
So just add something like this:
LayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setAutoMeasureEnabled(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setNestedScrollingEnabled(false);
UPDATE 2
Since 27.1.0 setAutoMeasureEnabled
is deprecated, so you should provide custom implementation of LayoutManager with overridden method isAutoMeasureEnabled()
But after many cases of usage RecyclerView I strongly recommend not to use it in wrapping mode, cause this is not what it is intended for. Try to refactor whole your layout using normal single RecyclerView with several items' types. Or use approach with LinearLayout that I described below as last resort
Old answer (not recommended)
You can use RecyclerView
inside NestedScrollView
.
First of all you should implement your own custom LinearLayoutManager
, it makes your RecyclerView
to wrap its content.
For example:
public class WrappingLinearLayoutManager extends LinearLayoutManager
{
public WrappingLinearLayoutManager(Context context) {
super(context);
}
private int[] mMeasuredDimension = new int[2];
@Override
public boolean canScrollVertically() {
return false;
}
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
if (getOrientation() == HORIZONTAL) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
heightSpec,
mMeasuredDimension);
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
measureScrapChild(recycler, i,
widthSpec,
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view.getVisibility() == View.GONE) {
measuredDimension[0] = 0;
measuredDimension[1] = 0;
return;
}
// For adding Item Decor Insets to view
super.measureChildWithMargins(view, 0, 0);
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(
widthSpec,
getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view),
p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(
heightSpec,
getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view),
p.height);
view.measure(childWidthSpec, childHeightSpec);
// Get decorated measurements
measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
After that use this LayoutManager
for your RecyclerView
recyclerView.setLayoutManager(new WrappingLinearLayoutManager(getContext()));
But you also should call those two methods:
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);
Here setNestedScrollingEnabled(false)
disable scrolling for RecyclerView
, so it doesn't intercept scrolling event from NestedScrollView
. And setHasFixedSize(false)
determine that changes in adapter content can change the size of the RecyclerView
Important note: This solution is little buggy in some cases and has problems with perfomance, so if you have a lot of items in your RecyclerView
I'd recommend to use custom LinearLayout
-based implementation of list view, create analogue of Adapter for it and make it behave like ListView
or RecyclerView
Solution 3 - Android
-
You need to use support library 23.2.0 (or) above
-
and
RecyclerView
height will bewrap_content
. -
recyclerView.setNestedScrollingEnabled(false)
But by doing this the recycler pattern doesn't work. (i.e all the views will be loaded at once because wrap_content
needs the height of complete RecyclerView
so it will draw all child Views
at once. No view will be recycled). Try not to use this pattern unless it is really required. Try to use viewType
and add all other views that need to scroll to RecyclerView
rather than using RecyclerView
in Scrollview
. The performance impact will be very high.
To make it simple "it just acts as LinearLayout
with all the child views"
Solution 4 - Android
You can use android:fillViewport="true"
to make NestedScrollView
measure the RecyclerView
. The RecyclerView
will fill the remaining height. so if you want to scroll the NestScrollView
, you can set the RecyclerView
's minHeight
.
Solution 5 - Android
Simply adding recyclerView.setNestedScrollingEnabled(false);
before setAdapter
itself worked for me. I didn't add app:layout_behavior="@string/appbar_scrolling_view_behavior"
anywhere & didn't set any custom layout manager
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:text="Some Text..."
android:padding="15dp" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:padding="15dp"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Quick Links"
android:textColor="@color/black"
android:textStyle="bold"
android:textAllCaps="true"
android:paddingLeft="20dp"
android:drawableLeft="@drawable/ic_trending_up_black_24dp"
android:drawablePadding="10dp"
android:layout_marginBottom="10dp"
android:textSize="16sp"/>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="#efefef"/>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Solution 6 - Android
This is what working for me
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.v4.widget.NestedScrollView>
Solution 7 - Android
For androidx
it's called androidx.core.widget.NestedScrollView
- and it scrolls alike butter with properties isScrollContainer
and measureAllChildren
enabled:
<!-- Scrolling Content -->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isScrollContainer="true"
android:measureAllChildren="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fastScrollEnabled="true"
android:scrollbarStyle="insideInset"
android:scrollbars="vertical"
android:splitMotionEvents="false"
android:verticalScrollbarPosition="right"/>
</androidx.core.widget.NestedScrollView>
Solution 8 - Android
There is a simple and testing code u may check
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.NestedScrollView>
Solution 9 - Android
I used RecyclerView inside a NestedScrollView and it worked for me. The only gotcha I had to keep in mind was that a NestedScrollView takes only one child view. So in my case I used of LienearLayout viewgroup which was housing my RecyclerView plus a number of other views that I needed.
I experience one issue putting my RecyclerView inside the NestedScrollView. I realized that scrolling the content of my RecyclerView slacked.
I later realized that my RecyclerView was receiving the scrolling event and therefore was conflicting with the scrolling behavior of the NestedScrollView.
So to solve that problem, I had to disable the scroll functionality of my RecyclerView with this method movieListNewRecyclerView.setNestedScrollingEnabled(false);
You can checkout my Instagram for a short video of what I actually did. This is my instagram handle ofelix03
Solution 10 - Android
Try to use this library - https://github.com/serso/android-linear-layout-manager.
LayoutManager of the library makes RecyclerView wraps its contents. In this case RecyclerView will be "as big as inner views", so it will not have a scrollbars and user will use scrolling abilities of NestedScrollView. Therefore, it will not be ambiguous like "scrollable inside scrollable".
Solution 11 - Android
Here is the code that I'm using to avoid scrolling issues:
mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.getLayoutManager().setAutoMeasureEnabled(true);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setHasFixedSize(false);
Solution 12 - Android
I have Viewpager and RecyclerView inside the NestedScrollView. After adding below lines
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);
I solved slow scroll and scroll lag issue.
Solution 13 - Android
if you want to use RecyclerView in NestedScrollView this is a simple tricky, just set :
RecyclerView
-
recyclerView.setHasFixedSize(false) (java/kt)
-
android:nestedScrollingEnabled="false"
-
android:layout_height="wrap_content"
-
android:overScrollMode="never"
NestedScrollView
- android:fillViewport="true"
this is work for me, and you can use many RecyclerView in NestedScrollView with this too.
Solution 14 - Android
If you are using RecyclerView-23.2.1
or later. Following solution will work just fine:
In your layout add RecyclerView like this:
<android.support.v7.widget.RecyclerView
android:id="@+id/review_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
And in your java file:
RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager=new LinearLayoutManager(getContext());
layoutManager.setAutoMeasureEnabled(true);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(new YourListAdapter(getContext()));
Here layoutManager.setAutoMeasureEnabled(true);
will do the trick.
Check out this issue and this developer blog for more information.
Solution 15 - Android
If you are using RecyclerView ScrollListener inside NestedScrollView, addOnScrollListener listener not working properly if you are used both.
Use this code.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
......
}
});
this code working fine RecyclerView ScrollListener inside NestedScrollView.
thanks
Solution 16 - Android
You can't use a recycler view within a nested scroll view. It's not intended to contain further scrollable views but it's because it's a child of a scrolling layout itself that you need the nested scrolling view. I had the same issue but in the end I moved my textview to be a headerview within the recyclerview, made the recyclerview a direct child of the coordinator layout and deleted the nested scroll view. Then all my problems were gone.
Solution 17 - Android
One solution to keep the recycling feature of the recyclerview and avoiding the recyclerview to load all your data is setting a fix height in the recyclerview itself. By doing this the recyclerview is limited only to load as much as its height can show the user thus recycling its element if ever you scroll to the bottom/top.
Solution 18 - Android
There are a lot of good answers. The key is that you must set nestedScrollingEnabled
to false
. As mentioned above you can do it in java code:
mRecyclerView.setNestedScrollingEnabled(false);
But also you have an opportunity to set the same property in xml code (android:nestedScrollingEnabled="false"
):
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:nestedScrollingEnabled="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Solution 19 - Android
I had to implement CoordinatorLayout with toolbar scrolling and it just took me all the day messing around this. I've got it working by removing NestedScrollView at all. So I'm just using RelativeLayout at the root.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.v7.widget.RecyclerView
android:id="@+id/rv_nearby"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>
Solution 20 - Android
do not use recyclerView inside NestedScrollView. it may cause cascading problems! I suggest using ItemViewTypes in RecyclerView for handling multiple kinds of views. just add a RecyclerView with match_parent width and height. then in your recyclerViewAdapter override getItemViewType and use position for handling what layout to be inflated. after that you can handle your view holder by using onBindViewHolder method.
Solution 21 - Android
nestedScrollView.setNestedScrollingEnabled(true);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//...
}
});
<androidx.core.widget.NestedScrollView
android:id="@+id/nested"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:layout_below="@id/appBarLayout_orders"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout ...
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
Solution 22 - Android
In my case, this is what works for me
-
Put
android:fillViewport="true"
inside NestedScrollView. -
Make the height of RecyclerView to wrap_content, i.e
android:layout_height="wrap_content"
-
Add this in RecyclerView
android:nestedScrollingEnabled="false"
OR
Programmatically, in your Kotlin class
recyclerView.isNestedScrollingEnabled = false
mRecyclerView.setHasFixedSize(false)
Solution 23 - Android
I have used this awesome extension (written in kotlin but can be also used in Java)
https://github.com/Widgetlabs/expedition-nestedscrollview
Basically you get the NestedRecyclerView
inside any package lets say utils in your project, then just create your recyclerview like
<com.your_package.utils.NestedRecyclerView
android:id="@+id/rv_test"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Check this awesome article by Marc Knaup
Solution 24 - Android
For my case the child of NestedScrollview is ConstraintLayout. It is not working as expected i replaced it to LinearLayout. Maybe it helps someone.
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Solution 25 - Android
At least as far back as Material Components 1.3.0-alpha03, it doesn't matter if the RecyclerView is nested (in something other than a ScrollView or NestedScrollView). Just put app:layout_behavior="@string/appbar_scrolling_view_behavior"
on its top level parent that's a sibling of the AppBarLayout in the CoordinatorLayout.
This has been working for me when using a single Activity architecture with Jetpack Naviagation, where all Fragments are sharing the same AppBar from the Activity's layout. I make the FragmentContainer the direct child of the CoordinatorLayout that also contains the AppBarLayout, like below. The RecyclerViews in the various fragments are scrolling normally and the AppBar folds away and reappears as expected.
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"/>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_scrollFlags="scroll|enterAlways|snap" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
liftOnScroll
(used to for app bars to look like they have zero elevation when at the top of the page) works if each fragment passes the ID of its RecyclerView to AppBarLayout.liftOnScrollTargetViewId
in Fragment.onResume
. Or pass 0 if the Fragment doesn't scroll.
Solution 26 - Android
RecyclerView with NestedScrollView
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />