343

I'm trying to implement a layout which contains RecyclerView and ScrollView at the same layout.

Layout template:

<RelativeLayout> <ScrollView android:id="@+id/myScrollView"> <unrelated data>...</unrealated data> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/my_recycler_view" /> </ScrollView> </RelativeLayout> 

Problems: I can scroll until the last element of ScrollView.

Things I tried:

  1. Card view inside the ScrollView (now ScrollView contains RecyclerView) - can see the card up until the RecyclerView.
  2. Initial thought was to implement this ViewGroup using RecyclerView instead of ScrollView where one of it's views type is the CardView, but I got the exact same results as with the ScrollView.
11
  • checkout this approach: stackoverflow.com/a/21878703/684582 Commented Jan 26, 2015 at 9:42
  • 18
    a simple solution in many of these cases is to use NestedScrollView instead, as it handles a lot of scrolling issues Commented Feb 19, 2016 at 14:21
  • 1
    Richard gave you the answer in February. Use a NestedScrollView instead of a ScrollView. That's exactly what it's for. Commented May 21, 2016 at 23:42
  • 3
    Doesn't change a thing for me. Commented Jun 1, 2016 at 6:17
  • 2
    For future reference, if anybody is experiencing similar issue only marshmallow/nougat (API 23, 24) devices, check my workaround at stackoverflow.com/a/38995399/132121 Commented Aug 17, 2016 at 11:32

26 Answers 26

823
+100

use NestedScrollView instead of ScrollView

Please go through NestedScrollView reference document for more information.

and add recyclerView.setNestedScrollingEnabled(false); to your RecyclerView

Sign up to request clarification or add additional context in comments.

15 Comments

Works with : android.support.v4.widget.NestedScrollView
keep android:layout_height="wrap_content" for the layout inflated for ViewHolder
In a complex layout NestedScrollView lags for me, unlike the ScrollView. Searching for a solution without using NestedScrollView
Also you can add android:nestedScrollingEnabled="false" to XML instead of recyclerView.setNestedScrollingEnabled(false);
It worked for me but keep in mind that the items inside recyclerView are not getting recycled.
|
120

I know I am late it the game, but the issue still exists even after google has made fix on the android.support.v7.widget.RecyclerView

The issue I get now is RecyclerView with layout_height=wrap_content not taking height of all the items issue inside ScrollView that only happens on Marshmallow and Nougat+ (API 23, 24, 25) versions.
(UPDATE: Replacing ScrollView with android.support.v4.widget.NestedScrollView works on all versions. I somehow missed testing accepted solution. Added this in my github project as demo.)

After trying different things, I have found workaround that fixes this issue.

Here is my layout structure in a nutshell:

<ScrollView> <LinearLayout> (vertical - this is the only child of scrollview) <SomeViews> <RecyclerView> (layout_height=wrap_content) <SomeOtherViews> 

The workaround is the wrap the RecyclerView with RelativeLayout. Don't ask me how I found this workaround!!! ¯\_(ツ)_/¯

<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:descendantFocusability="blocksDescendants"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> 

Complete example is available on GitHub project - https://github.com/amardeshbd/android-recycler-view-wrap-content

Here is a demo screencast showing the fix in action:

Screencast

14 Comments

Thanks man.. its help a lot.. but scrolling become hard after your solution so i set recyclerview.setNestedScrollingEnabled(false); and now its work like a charm.
Is this method recycle views? if we have around hundreds of object to be recycle. This is hack not solution.
Yes, @androidXP is right, this hack is not a solution for a long list. My use case was fixed item in a list view which was less than 10. And as for how I found the other workaround, I was trying random things, this was one of them :-)
if I use this solution, then onBindView is getting called for all the items in the list, which is not the usecase of recyclerview.
@HossainKhan thank you. The fix without the nestedscroll worked for me, however its worth pointing out that ONLY RelativeLayout works, I tested Linear and Constraint and it wont work for some reason.
|
57

Although the recommendation that

you should never put a scrollable view inside another scrollable view

Is a sound advice, however if you set a fixed height on the recycler view it should work fine.

If you know the height of the adapter item layout you could just calculate the height of the RecyclerView.

int viewHeight = adapterItemSize * adapterData.size(); recyclerView.getLayoutParams().height = viewHeight; 

13 Comments

How to get adapterItemSize in recyclerview any idea?
It works like a charm! Little correction it should be : int viewHeight = adapterItemSize * adapterData.size(); recyclerView.getLayoutParams().height = viewHeight;
How to find out adapterItemSize?
@JoakimEngstrom What is the adapterItemSize variable?
Avoid recycler view inside Scroll view, because scroll view give its child infinite space. This causes recycler view having wrap_content as height to measure infinitely in vertical direction(till the last item of recycler view). Instead of using recycler view inside scroll view, use only recycler view with different item types. With this implementation, children of scroll view would behave as as view type. Handle those viewtypes inside recycler view.
|
35

In case setting fixed height for the RecyclerView didn't work for someone (like me), here is what I've added to the fixed height solution:

mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { int action = e.getAction(); switch (action) { case MotionEvent.ACTION_MOVE: rv.getParent().requestDisallowInterceptTouchEvent(true); break; } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } }); 

5 Comments

Indeed, this worked well for me. With this in place, I was able to move the scroll view up and down, and when I select the recyclerview for scrolling, it takes priority over the scroll view. Thanks for the tip
it worked for me too, just be sure to use getParent on a direct child of the scrollview
This method works on cyanogenmod distributions as well. The fixed height solution on cyanogenmod works, but only if the fixed height is the absolute height of all of the items in the list, which defies the point of using the recyclerview in the first place. Upvoted.
I also needed recyclerView.setNestedScrollingEnabled(false);
This really worked! It never occurred to me to set a touch listener at the item level. I tried (with no luck) to set a touch listener at the recyclerView level. Great solution. Cheers!
28

The new Android Support Library 23.2 solves that problem, you can now set wrap_content as the height of your RecyclerView and works correctly.

Android Support Library 23.2

3 Comments

doesn't fling properly too (23.4.0)
@behelit 23.4.0 have some issues code.google.com/p/android/issues/detail?id=210085#makechanges , use 23.2.1 instead
doesnt even fling properly on 25.0.1
18

RecyclerViews are fine to put in ScrollViews so long as they aren't scrolling themselves. In this case, it makes sense to make it a fixed height.

The proper solution is to use wrap_content on the RecyclerView height and then implement a custom LinearLayoutManager that can properly handle the wrapping.

Copy this LinearLayoutManager into your project: link

Then wrap the RecyclerView:

<android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content"/> 

And set it up like so:

RecyclerView list = (RecyclerView)findViewById(R.id.list); list.setHasFixedSize(true); list.setLayoutManager(new com.example.myapp.LinearLayoutManager(list.getContext())); list.setAdapter(new MyViewAdapter(data)); 

Edit: This can cause complications with scrolling because the RecyclerView can steal the ScrollView's touch events. My solution was just to ditch the RecyclerView in all and go with a LinearLayout, programmatically inflate subviews, and add them to the layout.

1 Comment

Couldn't you call setNestedScrollingEnabled(false) on the recyclerview?
17

For ScrollView, you could use fillViewport=true and make layout_height="match_parent" as below and put RecyclerView inside:

<ScrollView android:fillViewport="true" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/llOptions"> <android.support.v7.widget.RecyclerView android:id="@+id/rvList" android:layout_width="match_parent" android:layout_height="wrap_content" /> </ScrollView> 

No further height adjustment needed through code.

3 Comments

Tested working fine using v23.2.1 . Was using it for adding layout above recyclerview.
just the RecyclerView scrolling and the ScrollView not scrolling
Doesn't work , as it creates&binds to all items in the RecyclerView
15

If you put RecyclerView inside NestedScrollView and enable recyclerView.setNestedScrollingEnabled(false);, scrolling will working well.
However, there is a problem

RecyclerView don't recycle

For example, your RecyclerView (inside NestedScrollView or ScrollView) have 100 item.
When Activity launch, 100 item will create (onCreateViewHolder and onBindViewHolder of 100 item will called at same time).
Example, for each item, you will load a large image from API => activity created -> 100 image will load.
It make starting Activity slowness and lagging.
Possible solution:
- Thinking about using RecyclerView with multiple type.

However, if in your case, there are just a few item in RecyclerView and recycle or don't recycle don't affect performance a lot, you can use RecyclerView inside ScrollView for simple

3 Comments

Show how can I make RecyclerView recycle even inside ScollView? Thanks!
@Liar, currently there is no way to make RecyclerView recycle after put it to ScrollView. If you want recycle, thinking about another approach (like using RecyclerView with multiple type)
you can give the recycler view a set height
14

Try this. Very late answer, but surely helps anyone in future.

  1. Change your ScrollView to NestedScrollView:

    <android.support.v4.widget.NestedScrollView> <android.support.v7.widget.RecyclerView ... ... /> </android.support.v4.widget.NestedScrollView> 
  2. In your UI code, update it for Recyclerview:

    recyclerView.setNestedScrollingEnabled(false); recyclerView.setHasFixedSize(false); 

2 Comments

using RecyclerView inside NestedScrollView is calling onBindView for every item in the list even if the item is not visible. Any solution for that problem?
You just need to give PaddingBottom into LinearLayout that is inside nestedScrollView - @thedarkpassenger
14

Calculating RecyclerView's height manually is not good, better is to use a custom LayoutManager.

The reason for above issue is any view which has it's scroll (ListView, GridView, RecyclerView) failed to calculate it's height when add as a child in another view has scroll. So overriding its onMeasure method will solve the issue.

Please replace the default layout manager with the below:

public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager { private static boolean canMakeInsetsDirty = true; private static Field insetsDirtyField = null; private static final int CHILD_WIDTH = 0; private static final int CHILD_HEIGHT = 1; private static final int DEFAULT_CHILD_SIZE = 100; private final int[] childDimensions = new int[2]; private final RecyclerView view; private int childSize = DEFAULT_CHILD_SIZE; private boolean hasChildSize; private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS; private final Rect tmpRect = new Rect(); @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(Context context) { super(context); this.view = null; } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); this.view = null; } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(RecyclerView view) { super(view.getContext()); this.view = view; this.overScrollMode = ViewCompat.getOverScrollMode(view); } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) { super(view.getContext(), orientation, reverseLayout); this.view = view; this.overScrollMode = ViewCompat.getOverScrollMode(view); } public void setOverScrollMode(int overScrollMode) { if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER) { throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode); } if (this.view == null) throw new IllegalStateException("view == null"); this.overScrollMode = overScrollMode; ViewCompat.setOverScrollMode(view, overScrollMode); } public static int makeUnspecifiedSpec() { return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); } @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); final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED; final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED; final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY; final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY; final int unspecified = makeUnspecifiedSpec(); /** * In case of exact calculations for both dimensions let's * use default "onMeasure" implementation. */ if (exactWidth && exactHeight) { super.onMeasure(recycler, state, widthSpec, heightSpec); return; } final boolean vertical = getOrientation() == VERTICAL; initChildDimensions(widthSize, heightSize, vertical); int width = 0; int height = 0; /** * It's possible to get scrap views in recycler which are bound to old (invalid) * adapter entities. This happens because their invalidation happens after "onMeasure" * method. As a workaround let's clear the recycler now (it should not cause * any performance issues while scrolling as "onMeasure" is never called whiles scrolling). */ recycler.clear(); final int stateItemCount = state.getItemCount(); final int adapterItemCount = getItemCount(); /** * Adapter always contains actual data while state might contain old data * (f.e. data before the animation is done). As we want to measure the view * with actual data we must use data from the adapter and not from the state. */ for (int i = 0; i < adapterItemCount; i++) { if (vertical) { if (!hasChildSize) { if (i < stateItemCount) { /** * We should not exceed state count, otherwise we'll get IndexOutOfBoundsException. * For such items we will use previously calculated dimensions. */ measureChild(recycler, i, widthSize, unspecified, childDimensions); } else { logMeasureWarning(i); } } height += childDimensions[CHILD_HEIGHT]; if (i == 0) { width = childDimensions[CHILD_WIDTH]; } if (hasHeightSize && height >= heightSize) { break; } } else { if (!hasChildSize) { if (i < stateItemCount) { /** * We should not exceed state count, otherwise we'll get IndexOutOfBoundsException. * For such items we will use previously calculated dimensions. */ measureChild(recycler, i, unspecified, heightSize, childDimensions); } else { logMeasureWarning(i); } } width += childDimensions[CHILD_WIDTH]; if (i == 0) { height = childDimensions[CHILD_HEIGHT]; } if (hasWidthSize && width >= widthSize) { break; } } } if (exactWidth) { width = widthSize; } else { width += getPaddingLeft() + getPaddingRight(); if (hasWidthSize) { width = Math.min(width, widthSize); } } if (exactHeight) { height = heightSize; } else { height += getPaddingTop() + getPaddingBottom(); if (hasHeightSize) { height = Math.min(height, heightSize); } } setMeasuredDimension(width, height); if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) { final boolean fit = (vertical && (!hasHeightSize || height < heightSize)) || (!vertical && (!hasWidthSize || width < widthSize)); ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS); } } private void logMeasureWarning(int child) { if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." + "To remove this message either use #setChildSize() method or don't run RecyclerView animations"); } } private void initChildDimensions(int width, int height, boolean vertical) { if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) { /** Already initialized, skipping. */ return; } if (vertical) { childDimensions[CHILD_WIDTH] = width; childDimensions[CHILD_HEIGHT] = childSize; } else { childDimensions[CHILD_WIDTH] = childSize; childDimensions[CHILD_HEIGHT] = height; } } @Override public void setOrientation(int orientation) { /** Might be called before the constructor of this class is called. */ //noinspection ConstantConditions if (childDimensions != null) { if (getOrientation() != orientation) { childDimensions[CHILD_WIDTH] = 0; childDimensions[CHILD_HEIGHT] = 0; } } super.setOrientation(orientation); } public void clearChildSize() { hasChildSize = false; setChildSize(DEFAULT_CHILD_SIZE); } public void setChildSize(int childSize) { hasChildSize = true; if (this.childSize != childSize) { this.childSize = childSize; requestLayout(); } } private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) { final View child; try { child = recycler.getViewForPosition(position); } catch (IndexOutOfBoundsException e) { if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e); } return; } final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams(); final int hPadding = getPaddingLeft() + getPaddingRight(); final int vPadding = getPaddingTop() + getPaddingBottom(); final int hMargin = p.leftMargin + p.rightMargin; final int vMargin = p.topMargin + p.bottomMargin; /** We must make insets dirty in order calculateItemDecorationsForChild to work. */ makeInsetsDirty(p); /** This method should be called before any getXxxDecorationXxx() methods. */ calculateItemDecorationsForChild(child, tmpRect); final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child); final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child); final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally()); final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically()); child.measure(childWidthSpec, childHeightSpec); dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin; dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin; /** As view is recycled let's not keep old measured values. */ makeInsetsDirty(p); recycler.recycleView(child); } private static void makeInsetsDirty(RecyclerView.LayoutParams p) { if (!canMakeInsetsDirty) return; try { if (insetsDirtyField == null) { insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty"); insetsDirtyField.setAccessible(true); } insetsDirtyField.set(p, true); } catch (NoSuchFieldException e) { onMakeInsertDirtyFailed(); } catch (IllegalAccessException e) { onMakeInsertDirtyFailed(); } } private static void onMakeInsertDirtyFailed() { canMakeInsetsDirty = false; if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect"); } } } 

1 Comment

How can i call this class when we set the data to adapter?
10

UPDATE: this answer is out dated now as there are widgets like NestedScrollView and RecyclerView that support nested scrolling.

you should never put a scrollable view inside another scrollable view !

i suggest you make your main layout recycler view and put your views as items of recycler view.

take a look at this example it show how to use multiple views inside recycler view adapter. link to example

4 Comments

i have a page with more than one Recycler, is there any other way to persuade this? some thing like instagram or google play part comment that load more record when you click on more comment
make it a single recyclerView and put your views as items for that recycler
That is nonsense. You can have nested RecyclerViews just fine.
This answer is now outdated. We have things like NestedScrollView that allow for nested scrollable views. Nested RecyclerViews also now work.
6

Add this line to your RecyclerView xml view:

android:nestedScrollingEnabled="false" 

And your RecyclerView will be smoothly scrolled with flexible height.

Hope it helps.

Comments

6

It seems that NestedScrollView does solve the problem.

I've tested using this layout:

<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dummy_text" /> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </android.support.v7.widget.CardView> </LinearLayout> </android.support.v4.widget.NestedScrollView> 

And it works without issues.

3 Comments

Bro, Still have the Same Problem After changed into NestedScrollview from the Scrollview.
mmm...can you share some code...I'm having zero issues but u can never know with this kind of issues
using this code calls onBindView for all the items in the list even if those items are not visible in the list. This defeats the purpose of recyclerview.
3

I was having the same problem. That's what i tried and it works. I am sharing my xml and java code. Hope this will help someone.

Here is the xml

<?xml version="1.0" encoding="utf-8"?> 

< NestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_thumbnail" android:layout_width="match_parent" android:layout_height="200dp" /> <TextView android:id="@+id/tv_description" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Description" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Buy" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Reviews" /> <android.support.v7.widget.RecyclerView android:id="@+id/rc_reviews" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView> </LinearLayout> </NestedScrollView > 

Here is the related java code. It works like a charm.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setNestedScrollingEnabled(false); 

4 Comments

What about layouts below RecyclerView?
that works as well. You just need to use. NestedScrollView instead of scroll view
Yes, only NestedScrollView. But solution with NestedScrollView+RecycleView call very slow populations of RecyclerView (I think for calcutaion of Y-position of first view after RecyclerView)
Doesn't work , as it creates&binds to all items in the RecyclerView
2

I used CustomLayoutManager to disable RecyclerView Scrolling. Also don't use Recycler View as WrapContent, use it as 0dp, Weight=1

public class CustomLayoutManager extends LinearLayoutManager { private boolean isScrollEnabled; // orientation should be LinearLayoutManager.VERTICAL or HORIZONTAL public CustomLayoutManager(Context context, int orientation, boolean isScrollEnabled) { super(context, orientation, false); this.isScrollEnabled = isScrollEnabled; } @Override public boolean canScrollVertically() { //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll return isScrollEnabled && super.canScrollVertically(); } } 

Use CustomLayoutManager in RecyclerView:

CustomLayoutManager mLayoutManager = new CustomLayoutManager(getBaseActivity(), CustomLayoutManager.VERTICAL, false); recyclerView.setLayoutManager(mLayoutManager); ((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); recyclerView.setAdapter(statsAdapter); 

UI XML:

<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background_main" android:fillViewport="false"> <LinearLayout android:id="@+id/contParentLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <edu.aku.family_hifazat.libraries.mpchart.charts.PieChart android:id="@+id/chart1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/x20dp" android:minHeight="@dimen/x300dp"> </edu.aku.family_hifazat.libraries.mpchart.charts.PieChart> </FrameLayout> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </android.support.v7.widget.RecyclerView> </LinearLayout> </ScrollView> 

Comments

1

Actually the main purpose of the RecyclerView is to compensate for ListView and ScrollView. Instead of doing what you're actually doing: Having a RecyclerView in a ScrollView, I would suggest having only a RecyclerView that can handle many types of children.

4 Comments

This would work only provided that your children can be garbaged collected as soon as you scroll them out of view. If you have children that are mapFragments or streetviews, it doesn't make sense as they are forced to reload each time they scroll off the recyclerview. Embedded them into a scrollview and then generating a recyclerview at the bottom makes more sense then.
@Simon there's handy setIsRecyclable() in ViewHolder
RecyclerView replaces ListView it is not meant to replace ScrollView.
This comment deserves better. It is a solution, and even better than the accepted one.
1

This does the trick:

recyclerView.setNestedScrollingEnabled(false); 

Comments

1

For those people who trying to do it just for design purposes - leave it. Redesign your app and leave only RecyclerView. It will be better solution than doing ANY hardcode.

Comments

0

First you should use NestedScrollView instead of ScrollView and put the RecyclerView inside NestedScrollView.

Use Custom layout class to measure the height and width of screen:

public class CustomLinearLayoutManager extends LinearLayoutManager { public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @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(i, View.MeasureSpec.UNSPECIFIED), heightSpec, mMeasuredDimension); width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { measureScrapChild(recycler, i, widthSpec, View.MeasureSpec.makeMeasureSpec(i, 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); recycler.bindViewToPosition(view, position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

And implement below code in the activity/fragment of RecyclerView:

 final CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(mAdapter); recyclerView.setNestedScrollingEnabled(false); // Disables scrolling for RecyclerView, CustomLinearLayoutManager used instead of MyLinearLayoutManager recyclerView.setHasFixedSize(false); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int visibleItemCount = layoutManager.getChildCount(); int totalItemCount = layoutManager.getItemCount(); int lastVisibleItemPos = layoutManager.findLastVisibleItemPosition(); Log.i("getChildCount", String.valueOf(visibleItemCount)); Log.i("getItemCount", String.valueOf(totalItemCount)); Log.i("lastVisibleItemPos", String.valueOf(lastVisibleItemPos)); if ((visibleItemCount + lastVisibleItemPos) >= totalItemCount) { Log.i("LOG", "Last Item Reached!"); } } }); 

1 Comment

It gets me an exception of IndexOutOfBoundsException: Invalid item position 0(0) ... CustomLinearLayoutManager.measureScrapChild(CustomLinearLayoutManager.java:68) . That's even though the items count isn't 0. Can you please post this sample on github?
0

If RecyclerView showing only one row inside ScrollView. You just need to set height of your row to android:layout_height="wrap_content".

Comments

0

Sorry being late to the party, but it seems like there is another solution which works perfectly for the case you mentioned.

If you use a recycler view inside a recycler view, it seems to work perfectly fine. I have personally tried and used it, and it seems to give no slowness and no jerkyness at all. Now I am not sure if this is a good practice or not, but nesting multiple recycler views , even nested scroll view slows down. But this seems to work nicely. Please give it a try. I am sure nesting is going to be perfectly fine with this.

Comments

0

Another approach to address the issue is to use ConstraintLayout inside ScrollView:

<ScrollView> <ConstraintLayout> (this is the only child of ScrollView) <...Some Views...> <RecyclerView> (layout_height=wrap_content) <...Some Other Views...> 

But I would still stick to the androidx.core.widget.NestedScrollView approach, proposed by Yang Peiyong.

Comments

0

You can try with setting recycler view Hight as wrap_content. in my case its working fine. I am trying with 2 different recycler view in scroll view

Comments

0

The best solution is to keep multiple Views in a Single View / View Group and then keep that one view in the SrcollView. ie.

Format -

<ScrollView> <Another View> <RecyclerView> <TextView> <And Other Views> </Another View> </ScrollView> 

Eg.

<ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:text="any text" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:text="any text" android:layout_width="match_parent" android:layout_height="wrap_content"/> </ScrollView> 

Another Eg. of ScrollView with multiple Views

<ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingHorizontal="10dp" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/CategoryItem" android:textSize="20sp" android:textColor="#000000" /> <TextView android:textColor="#000000" android:text="₹1000" android:textSize="18sp" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:textColor="#000000" android:text="so\nugh\nos\nghs\nrgh\n sghs\noug\nhro\nghreo\nhgor\ngheroh\ngr\neoh\n og\nhrf\ndhog\n so\nugh\nos\nghs\nrgh\nsghs\noug\nhro\n ghreo\nhgor\ngheroh\ngr\neoh\nog\nhrf\ndhog" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout> </ScrollView> 

Comments

0

You can also override LinearLayoutManager to make RecyclerView roll smoothly:

@Override public boolean canScrollVertically(){ return false; } 

Comments

-2

Solution which worked for me

Use NestedScrollView with height as wrap_content, and for your RecyclerView setup this:

<androidx.recyclerview.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="false" app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> 

And set view holder layout params:

android:layout_width="match_parent" android:layout_height="wrap_content" 

2 Comments

Some comments on a similar answer say disabling nested scrolling defeats the purpose of using a RecyclerView, i.e. that it won't recycle views. Not sure how to confirm that.
Setting nestedScrollingEnabled="false" causes RecyclerView to NOT recycle its views, at least in my setup (a RecyclerView inside a NestedScrollView). Confirmed by adding a RecyclerListener to the RecyclerView and setting a breakpoint inside its onViewRecycled() method.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.