@Jeel и @Birju показали вам правильный способ использования фрагмента, но я все же оставлю свой ответ на тот случай, если вы захотите глубже понять, почему ваша реализация не работает.
Причина:
Во-первых, заглядывая в main_layout:
<ConstraintLayout>
<fragment class="package.RecyclerFragment"
android:id="@+id/fragment"
... />
</ConstraintLayout>
Когда main_layout
раздувается в MainActivity, элемент <fragment>
просто заменяется на то, что включено в макет RecyclerFragment, то есть макет recycler_list
.
Таким образом, main_layout
на самом деле станет:
<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>
Если вы поместите этот код в onResume () в MainActivity, вы можете увидеть это ясно:
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}
Поэтому, когда вы вызываете эту строку:
fragmentManager?.beginTransaction()?.replace(R.id.fragment, HelloFragment())?.commit()
На самом деле RecyclerView воспринимается как контейнер группа представления и добавляет все, что в макете HelloFragment , в RecyclerView
В качестве доказательства вы можете взглянуть на эти строки в 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
Поскольку RecyclerView предназначен для хранения ViewHolders, созданных из данных в Adapter, он все еще сохраняетпеременная called childCount (в данном случае = 3) даже после того, как представление фрагмента Hello добавлено в RecyclerView и удалено из него все ViewHolders.
Когда добавлено новое представление, RecyclerView отправляет новый макет, который затем вызываетфункция с именем 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;
}
Как вы можете видеть, поскольку все ViewHolders были удалены, holder will be null
и NPE будут выброшены, когда дело доходит до строки if (holder.shouldIgnore()) {
СпасибоВы за чтение этого длинного ответа!