Fragment as a Recyclerview item
Using Android Kotlin
Foreword:For the new requirements of the former company’s project, the past pages need to be displayed in the Recyclerview, but they are all Fragments. Recyclerview originally did not support this approach. What we can do?
Method1: FragmentContainer
Step1 Set up XML:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Step2 Establish the corresponding Class of XML:
class FragmentContainerLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
lateinit var attachedActivity: AppCompatActivity
lateinit val containerId: Int
lateinit val fragment: Fragment
init {
inflate(context, R.layout.layout_fragment_container, this)
}
open fun initializeLayout() {
val oldFragment = attachedActivity.findFragment(containerId)
if (oldFragment != null) {
attachedActivity.removeFragment(oldFragment)
}
this.fragmentContainer.id = containerId
attachedActivity.replaceFragment(fragment,containerId)
}
}
In Recyclerview, each container must have its own unique id, otherwise it will continue to replace the same container, and remember its order, if the id changed will let app crashed. I use View.generateViewId() to generate the Id and use the original data source array to store
FragmentContainerData(
fragment = SignFragment(),
containerId = View.generateViewId()
)
Step3 Make item layout
<com.cartoonhero.source.stage.scenery.templates.FragmentContainerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.cartoonhero.source.stage.scenery.templates.FragmentContainerLayout>
And use it in RecyclerView adapter
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.view_fragment_container, parent, false)
Method2: ViewPager2 with single page
In fact, the above method ViewPager2 is actually done, and it’s convenience to expand pages by using ViewPager2, but it is not recommended to do this because you will face the complex life cycle and recycling problems of nested Fragments
Same with Method1 Steps:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
android:orientation="horizontal" />
</androidx.constraintlayout.widget.ConstraintLayout>
But this time we want to put in the parameters related to ViewPager2
var attachedActivity: AppCompatActivity? = null
val vpFragments = mutableListOf<Fragment>()
val fragmentIds = mutableListOf<Long>() // record unique id
private val createdIds = hashSetOf<Long>() // Record order,Use the hashSet feature to determine whether it is duplicate
Generate ids
private fun generateFragmentIds() {
createdIds.clear()
fragmentIds.clear()
if (vpFragments.isNotEmpty() == true) {
for (fragment in vpFragments!!) {
val newId = View.generateViewId()
fragmentIds.add(newId.toLong())
}
}
}
ViewPager2 will remove Fragment with repeated id, so we won’t do it by ourselves to avoid Fragment already added crash
Then build FragmentStateAdapter
private inner class VP2FragmentAdapter(activity: AppCompatActivity): FragmentStateAdapter(activity) {
override fun getItemCount(): Int {
return vpFragments.size
}
override fun containsItem(itemId: Long): Boolean {
return createdIds.contains(itemId)
}
override fun getItemId(position: Int): Long {
return fragmentIds[position]
}
override fun createFragment(position: Int): Fragment {
createdIds.add(fragmentIds[position])
return vpFragments[position]
}
}
Same with Method1 Step3
Result: