RecyclerView in Fragment crashes when navigating to another Fragment
@Jeel and @Birju showed you the right way to use fragment, but I still leave my answer in case you want to understand deeper why your implementation doesn't work.
Reason:
First, looking into main_layout:
<ConstraintLayout>
<fragment class="package.RecyclerFragment"
android:id="@+id/fragment"
... />
</ConstraintLayout>
When main_layout
is inflated in MainActivity, <fragment>
element is simply replaced by whatever included inside RecyclerFragment's layout a.k.a recycler_list
layout.
So main_layout
will actually become:
<ConstraintLayout>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/list"
android:orientation="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</ConstraintLayout>
If you put these code in onResume() in MainActivity, you can see it clearly:
override fun onResume() {
super.onResume()
val parent = findViewById<ConstraintLayout>(R.id.constraint)
val numChild = parent.childCount
val childView = parent.getChildAt(0)
Log.d("Parent", parent.toString())
Log.d("NumChild", numChild.toString())
Log.d("ChildView", childView.toString())
return
}
// Log
D/Parent: androidx.constraintlayout.widget.ConstraintLayout{a6e5545 V.E...... ......I. 0,0-0,0 #7f07004d app:id/constraint}
D/NumChild: 1
D/ChildView: androidx.recyclerview.widget.RecyclerView{753849a VFED..... ......I. 0,0-0,0 #7f070060 app:id/fragment}
Therefore, when you call this line:
fragmentManager?.beginTransaction()?.replace(R.id.fragment, HelloFragment())?.commit()
It actually took RecyclerView as the container view group and add whatever in HelloFragment's layout into RecyclerView
For evidence, you can take a look at these lines in FragmentManager class:
// mContainerId here is R.id.fragment in your layout
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
// Now container is RecyclerView
...
f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState)
// After this line, f.mView is the view inside hello.xml
...
container.addView(f.mView);
// Add layout in hello.xml into RecyclerView
Because RecyclerView is designed to hold ViewHolders created from data in Adapter, it still keep a variable called childCount (= 3 in this case) even after Hello's fragment view is added inside RecyclerView and removed all ViewHolders from it.
When new view added, RecyclerView dispatch new layout, which then call a function named findMinMaxChildLayoutPositions()
private void findMinMaxChildLayoutPositions(int[] into) {
final int count = mChildHelper.getChildCount();
...
for (int i = 0; i < count; ++i) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
if (holder.shouldIgnore()) {
continue;
}
As you can see, because all ViewHolders have been removed, holder will be null
and NPE will be thrown when it comes to line if (holder.shouldIgnore()) {
Thank you for reading this long answer!