RecyclerView as child of NestedScrollView - not recycling views - android

I am trying to create a scroll-able area which will contain various sections of the following types:
Horizontal Recycling Section
Vertical Recycling Section
Text Section
The approach I am taking is to have a NestedRecyclerView as the parent scroll view for all the child sections. This view looks like so:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/mynav_appbarLayout"
android:background="?attr/themeToolbarBg"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="#+id/mynav_toolbar"
layout="#layout/actionbar_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
Then, for each section type I am creating a corresponding view binding and adding it as a child to the LinearLayout which is inside the NestedScrollView.
There are 2 types of section layout, one which is a simple TextView (which I will omit here as it is not relevant) the other of which is a view which contains a RecyclerView. The layout manager for this RecyclerView is created dynamically depending on whether the section it is to be used for is a horizontal or vertical section.
The layout with the RecyclerView in looks like so:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:focusable="true"
android:focusableInTouchMode="true">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fooBarsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:nestedScrollingEnabled="false"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
</LinearLayout>
</layout>
Now, when I am adding these views to the parent NestedScrollView's LinearLayout and setting up the LayoutManager for the associated RecyclerView to orientation Horizontal it works fine, but, when I use orientation Vertical (which is the same orientation as the NestedScrollView) the RecyclerView is NOT recycling views. Obviously this is leading to unacceptable performance.
After doing about a days worth of research and banging my head against the wall it appears that having a RecyclerView nested within a NestedScrollView with the same orientation as the NestedScrollView causes the RecyclerView to lose it's recycler functionality.
As you can hopefully see from the above layout, I have tried all the suggestions I could find, making sure the RecyclerView's height is not wrap_content, using layout_behaviour, setting the NestedRecyclerView to fill view port and so on.
I have exhausted 6 pages of google search around this issue and have tried every suggestion I have found either on SO or blogs and nothing is working.
Oddly, if I swap out the NestedScrollView for a ScrollView, the vertical RecyclerView regains it's recycler functionality, but now scrolls independently of the parent ScrollView which doesn't meet our requirements.
Is this a solved problem or do I need to rethink my entire solution? I.e. am I just missing an attribute or doing something wrong in the XML or is it fundamentally an issue with using a RecyclerView inside a NestedScrollView with the same orientation?
Here is the list of resources, the suggestions of which I have tried exhaustively to no avail:
How to use RecyclerView inside NestedScrollView?
How to use RecyclerView inside NestedScrollView
Recycler view inside NestedScrollView causes scroll to start in the middle
https://android.jlelse.eu/recyclerview-within-nestedscrollview-scrolling-issue-3180b5ad2542
https://medium.com/#mujtahidah/load-more-recyclerview-inside-nested-scroll-view-and-coordinator-layout-4f179dc01fd
https://github.com/google/flexbox-layout/issues/400
https://www.reddit.com/r/androiddev/comments/8oj8cb/having_recyclerview_inside_a_nestedscrollview_is/
https://github.com/mikepenz/FastAdapter/issues/447
https://www.reddit.com/r/androiddev/comments/bixl6r/nestedscrollview_recyclerview/
View Recycling not happens with Multiple Recyclerview inside NestedScrollView
How to make RecyclerView do recycling inside NestedScrollView?
https://code-examples.net/en/q/1d90611
As per a suggestion in the comments, I could model this with a multi type adapter, which is something I have done before but for this particular problem I am not sure this approach will work.
I think the comment is suggesting I model it like so:
Where the adapter would adapt types:
Horizontal Section
Text Section
Card Section
But, the requirement is this:
So, as you can hopefully see, the RecyclerView will have a LinearLayoutManager with orientation Vertical, but, once we hit the cards, they have to be laid out in a grid fashion, which of course the LinearLayoutManager does not support. So, perhaps I can have the final section be another RecyclerView with a GridLayoutManager? But, I tried this last night and it didn't work, there were scrolling issues as the bottom most RecyclerView is scrolling vertically within the outermost RecyclerView which is also scrolling vertically.

Related

Groupie RecyclerView Expand Parent's Layout To Include Children

I've a expandable recyclerView made using Groupie which I would like to have the children included on the parent's same layout to make use of the borders for its card view. Not sure how to achieve that though.
Instead of
I would like to have this
This is the top of my parent's layout
<androidx.cardview.widget.CardView
android:id="#+id/cv_parent"
android:layout_width="match_parent"
android:layout_height="40dp"
(Tried with wrap content as well but couldn't see much difference)
And this is the layout for their children
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
Thanks very much.

Non-scrolling WRAP_CONTENT RecyclerView inside ScrollView in ConstraintLayout

I want to document the problems I ran into and the solution for the benefit of others, and find help with the key flaw in my solution.
I want a RecyclerView with an arbitrary number of rows in a layout with a number of other Views. The RecyclerView and other Views should be in a ScrollView that scrolls, but the RecyclerView itself should not scroll.
Since the number of rows in the RecyclerView is unknown, and I'm required to have other views immediately underneath the RecyclerView, I can't use a fixed height or match_parent.
I experienced some odd problems: when I would update the RecyclerView data (using an AsyncListDiffer) and the UI was supposed to update, the entire RecyclerView would jump above the view it was constrained underneath, straight to the top of the parent. This is not how a ConstraintLayout is supposed to behave.
Then I was able to stop that from happening, but Views underneath the RecyclerView would disappear--appearing once the RecyclerView data updated.
The solution:
Put the RecyclerView and other Views inside a ConstraintLayout inside a ScrollView (or NestedScrollView)
Set the RecyclerView to have a height of wrap_content (and adding app:layout_constrainedHeight="true" doesn't hurt)
Make sure the rows in the RecyclerView have a fixed height and do not have a height of wrap_content
This was helpful to me after banging my head against the wall and trying some proposed solutions that did not work: make RecyclerView's height to "wrap_content" in Constraint layout
This solution is pretty simple. The layout can be something like:
<ScrollView
android:id="#+id/scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- other Views -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/otherView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/anotherView"
/>
<!-- other Views -->
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
And then the layout for a row:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight">
<!-- other views -->
</androidx.constraintlayout.widget.ConstraintLayout>
Here's the problem: I'm not a fan of fixed-height rows. What if other content needs to go in there? What if the user changes their text size? I much prefer flexible layouts that can adapt in size. But if I do that, then the RecyclerView takes up a screen's worth of height inside the ScrollView, shoving all lower views off the bottom of the screen, no matter how few rows it contains.
The alternatives I can think of are to make all the other Views outside the RecyclerView become rows of the RecyclerView, or to avoid a RecyclerView altogether and programmatically add Views to a LinearLayout. These are much uglier approaches.
Is there a way to fix it so the rows of the RecyclerView can have a height of wrap_content?

Recyclerview binds all views at the same time

I have a vertical recyclerview (with a GridLayoutManager) inside another recyclerview (with LinearLayoutManager). The problem I am facing right now is that, the inner recyclerview (with GridLayoutManager) binds all of it's items at the same time, even the views that are not on the screen at the moment (onBindViewHolder() gets called for all of its items).
To give you more information, in my layout file, I put height of my recycler view as wrap_content.
I think the problem is, since there are 2 nested vertically recyclerviews, when the parent RV wants to measure its children and the children is another RV, in onMeasure() it computes the size needed for the entire RV, not just the portion that it wants to bind on the screen.
Any idea how to solve this?
Here is the layout file for my outer recyclerview:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/component_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
And here is the code for my inner recyclerview:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/gutter"
android:paddingBottom="#dimen/gutter">
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/gutter"
android:textSize="30sp"
android:textColor="#android:color/white"
android:fontFamily="sans-serif-thin"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
P.S.: I'm using this adapter delegate for my outer recyclerview:
https://github.com/sockeqwe/AdapterDelegates
I think nested recyclerviews are a very bad idea. When i try to scroll, which recyclerview has to respond the the scolling, the parrent or child.
That is why I think you are looking for the ExpandableListView? That's limited to only two levels of listings, but that sounds like it would work for your needs). It also solves the soling issue.
It would look something like this:
EDIT: even nested ExpandableListViews are possible:
EDIT: check this lib for horizontal scroling
This is a known bug.
You should not put a RecyclerView inside another RecyclerView because RecyclerView gives its children infinite space. Hence the inner RecyclerView keeps measuring till the dataset is exhausted. Try setting setAutoMeasureEnabled(false) to false on layout manager or you can solve this problem by using a wrapper adapter instead of inner recycler view.
The first thing you need to know is that, when you nest scrolling layouts, the inner ones will get infinity allowed height, effectively making them wrap_content. There is in fact a relatively easy way to fix this problem.
Say I had two nested RecyclerViews such as these, in this case vertically oriented.
<RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical>
<View .../>
<!-- other stuff -->
<RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
</RecyclerView>
The inner recyclerView here will bind all of it's children immediately every time because, from it's position, your screen will have infinite height.
The solution is to set the height of your inner recyclerview to some static value, not wrap_content or match parent, as either of those will simply fill up the outer recyclerview with one view that will all be bound at once due to it's large height. If you make the height of the inner recyclerview the same as the display's height, you should see your problem go away.
Here is an implementation that will not bind all children at once:
<RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical>
<View .../>
<!-- other stuff -->
<RecyclerView
android:layout_width="match_parent"
android:layout_height="#dimen/screen_height"
android:orientation="vertical"/>
</RecyclerView>
Note the layout_height of the inner RecyclerView is now a fixed value pulled from the dimensions file. You yourself will have to come up with a reasonable value to put there.
Side Note: In order to make all of this work and for scrolling to work properly, you may have to play around with the parameter: NestedScrollingEnabled in your RecyclerViews - there are several known bugs relating to this that you may need to work around.
i.e.: innerRecyclerView.setHasFixedSize(true); and innerRecyclerView.setNestedScrollingEnabled(false).
so what happens here when you place a scrollview(no fixed size because of wrap content) inside another scrollview(again no fixed size because of wrap content),both nested scroll view fails to render.
So there is two solutions--
1- Either you will have to think of alternative solution for nested scrollviews
2- You can give outside recyclerview cell fixed height so that inside recycler view can get some fixed layout to render itself.
I could solve my issue by using only one Recyclerview, where it has a grid layout, and based on the component items i'm adding into it, i change the spancount for that. Basically instead of adding the inner recyclerview, i add the items that were supposed to go to the inner recyclerview, to the outer recyclerview.

Layout Jerking when RecyclerView inside a ScrollView

Im creating RecyclerView and ListView inside ScrollView and im getting problems with the scroll.. The scroll is Jerking (unable to get smooth scroll), I know its the problem with the RecyclerView inside the ScrollView, because layout is scrolling without any problem when swiping until the ListView exists but once RecyclerView items enter the layout it starts to jerk( only scrolling with the finger, no proper scroll when finger is taken off). Here is the code in the xml
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/lv_home_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#color/colorWhite"
android:dividerHeight="0.5dp"
android:visibility="gone"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycleView"
android:background="#color/colorWhite">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</ScrollView>
Nested views that scroll along the same axis have always been
problematic on Android. Recently Google has added nested scrolling
support. In order to have this on older platform levels, you should
use the views in the support library like NestedScrollView and
RecyclerView.
ListView does not work with wrap_content as its height. You can do
this with RecyclerView if you have the latest version of the
RecyclerView support library. Besides, you are already using RecyclerView in one place, you might as well use them exclusively.
ListView is not really meant to be a "drop down". Perhaps you should consider a Spinner instead.
I have it finally.!!
Just add the following line of code in you class where you are calling the RecyclerView
mRecyclerView = (RecyclerView)tmpView.findViewById(R.id.recycleView);
mRecyclerView.setNestedScrollingEnabled(false);
It works for me!

Create a RecyclerView with both horizontal and vertical scrolling

Over the past few weeks I've been learning to use the RecyclerView. I need to implement a horizontal list, ie, that by turning the device in landscape mode like so:
I found the best solution for this (how to create the horizontal displacement of RecyclerView, here), but encountered another problem. The item RecyclerView was larger than the height of the device (in landscape, horizontal), so I need to create a vertical and horizontal displacement, simultaneously.
I looked at the Android Developer methods for the LayoutManager class, but my skills are not high enough to understand most of the methods. I also tried putting a RecyclerView vertically inside another RecyclerView horizontally with all the content, but I get error:
IllegalStateException: RecyclerView has no LayoutManager
To rememedy this I removed all <View ... /> elements from the XML file, but this does not give any results.
To clarify what I am asking: is it possible to have my layout scroll both horizontally and vertically, and if you could explain how I would appreciate it.
I was so angry about all the problems that had tended with the application that had not thought about the easiest solution.
In a RecyclerView consists of two XML files, the main one where the RecyclerView is declared and another with content.
The simplest solution was to introduce the RecyclerView within a ScrollView. So I can move all items at a time thanks to ScrollView vertically and horizontally I can move the items thanks to RecyclerView in landscape mode.
activity_main.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/cardIn_margin_ext">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbarStyle="outsideInset"
android:scrollbars="horizontal" />
</ScrollView>
The accepted answer did'nt work for me. I had to use the HorizontalScrollView instead of simple ScrollView.
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/cardIn_margin_ext">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbarStyle="outsideInset"
android:scrollbars="horizontal" />
</HorizontalScrollView >

Categories

Resources