I am new to Android development and am completing a project in which I am required to use Jetpack Navigation.
My app is a single-activity, multiple-fragment design.
I am also trying to use ViewPager2 to swipe between my "home" Fragment A, to Fragment B (also, I am using indicator dots to show the user where they are in the ViewPager). In future I will have a Fragment C, which I would like to navigate to with conventional navigation from Navigation B.
I am really struggling to figure out how to use navigation and ViewPager2 together harmoniously: at the moment, their co-existence is causing a duplicate instantiation of Fragment A, which leads to other bugs (chiefly, I collect a Flow in Fragment A and this also gets duplicated in undesirable ways).
This how my activity_main.xml looks:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:translationZ="2dp"
android:padding="0dp"
android:outlineProvider="none"
android:background="@color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/myNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tab_layout"
app:navGraph="@navigation/navigation" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:background="@drawable/background"
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_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I already hate the way this is set up, as it just feels wrong that the ViewPager does not contain the FragmentContainerView, but I'm not sure how else to do it since ViewPager2 does not allow child views.
And then, this is how I set things up in my MainActivity.kt:
const val FRAGMENT_COUNT = 2
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
private lateinit var viewPager: ViewPager2
private lateinit var tabLayout: TabLayout
private lateinit var pagerAdapter: PagerAdapter
private val viewModel: MainViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tabLayout = findViewById(R.id.tab_layout)
viewPager = findViewById(R.id.view_pager)
pagerAdapter = PagerAdapter(this)
viewPager.adapter = pagerAdapter
val navHostFragment = supportFragmentManager.findFragmentById(R.id.myNavHostFragment) as NavHostFragment
navController = navHostFragment.navController
TabLayoutMediator(tabLayout, viewPager) { _, _ -> }.attach()
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val currentDestinationId = navController.currentDestination?.id
when (position) {
0 -> {
if (currentDestinationId != R.id.newSuggestionFragment) {
navController.navigate(R.id.action_favouritesFragment_to_newSuggestionFragment)
}
}
1 -> {
if (currentDestinationId != R.id.favouritesFragment) {
navController.navigate(R.id.action_newSuggestionFragment_to_favouritesFragment)
}
}
}
}
})
viewModel.message.observe(this) {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
}
}
class PagerAdapter(fragmentActivity: FragmentActivity): FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int {
return FRAGMENT_COUNT
}
override fun createFragment(position: Int): Fragment =
when(position){
0 -> NewSuggestionFragment()
1 -> FavouritesFragment()
else -> NewSuggestionFragment()
}
}
I've tried researching online but I'm struggling to find clear information on this (most likely, I just don't understand the underlying issues well enough). I'd really appreciate any advice on fixing this.