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 View
s 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 View
s underneath the RecyclerView
would disappear--appearing once the RecyclerView
data updated.
The solution:
- Put the
RecyclerView
and otherView
s inside aConstraintLayout
inside aScrollView
(orNestedScrollView
) - Set the
RecyclerView
to have a height ofwrap_content
(and addingapp: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 View
s outside the RecyclerView
become rows of the RecyclerView
, or to avoid a RecyclerView
altogether and programmatically add View
s 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
?