Reputation: 1739
I'm using ViewPager2
with FragmentStateAdapter
. Basic 3 fragments (A-B-C). It means, you can scroll from A to B, B to C
What I want to do?
From fragment B start child fragment D
(A-B-C)
|
D
From fragment B start fragment D, when the user click "back arrow" (top bar), fragment D will be destroyed and the user will back to B. (I don't want to use activity)
What I tried?
val ft: FragmentTransaction = requireActivity().supportFragmentManager.beginTransaction()
ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
ft.replace(android.R.id.content, DetailsFragment())
ft.addToBackStack(null)
ft.commit();
With the code above I have two issues.
Edit:
Passing fragment manager to ViewPager2
, in main_activity in onCreate() method
pagerAdapter = PagerAdapter(supportFragmentManager, lifecycle)
binding.mainViewPager.adapter = pagerAdapter
PagerAdapter is a simple class:
class PagerAdapter(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle)
Upvotes: 6
Views: 4357
Reputation: 66879
This is answer for going detail fragment from a ViewPager's fragment as question states.
I also made it a little bit complicated by putting viewPager into a fragment, you can skip and add it to activity if you wish.
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
FragmentContainerView
contains the fragment that has ViewPager2
// Replace Fragment with DSL style
supportFragmentManager.commit {
replace<FragmentThatContainsViewPager>(R.id.fragment_container_view)
}
FragmentThatContainsViewPager's layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabMode="scrollable" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tabLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Code for container fragment
class FragmentThatContainsViewPager : BaseDataBindingFragment<FragmentWithViewpagerBinding>() {
override fun getLayoutRes(): Int {
return R.layout.fragment_with_viewpager
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// TabLayout
val tabLayout = dataBinding.tabLayout
// ViewPager2
val viewPager = dataBinding.viewPager
/*
🔥 Set Adapter for ViewPager inside this fragment using this Fragment,
more specifically childFragmentManager as param
*/
viewPager.adapter = ChildFragmentStateAdapter(this)
// Bind tabs and viewpager
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = "Tab $position"
}.attach()
}
}
Adapter of ViewPager, you can use GenericFragmentParent.newInstance() if you wish i used FragmentFactory
class ChildFragmentStateAdapter(private val fragment: Fragment) : FragmentStateAdapter(fragment) {
private val genericFragmentFactory = GenericFragmentFactory.getFragmentFactory()
override fun getItemCount(): Int = 5
override fun createFragment(position: Int): Fragment {
genericFragmentFactory.fragID = position
return genericFragmentFactory.instantiate(
fragment.requireActivity().classLoader,
GenericFragmentParent::class.java.name
)
}
}
class GenericFragmentParent(private val fragID: Int) :
BaseDataBindingFragment<FragmentGenericParentBinding>() {
override fun getLayoutRes(): Int {
return R.layout.fragment_generic_parent
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dataBinding.btnNextPage.setOnClickListener {
/*
🔥🔥 Replacing in container in Activity for both fragments to
be in same stack of supportFragmentManager
*/
requireActivity().supportFragmentManager.commit {
replace<GenericChildFragment>(R.id.fragment_container_view)
.addToBackStack(null)
}
}
}
}
Also used back stack count to display back arrow on top
supportFragmentManager.addOnBackStackChangedListener {
val fragmentOnTop = supportFragmentManager.findFragmentById(
R.id.fragment_container_view
)
val fragmentCount = supportFragmentManager.backStackEntryCount
supportActionBar?.setDisplayHomeAsUpEnabled(fragmentCount > 0)
}
And
override fun onSupportNavigateUp(): Boolean {
supportFragmentManager.popBackStack()
return true
}
to remove fragment when back arrow is touched.
I added sample here, it's in MaterialDesign module, probably add another example for navigation for child fragments of ViewPager tabs each with their own child fragments instead of opening detail fragment while you can swipe neighbor fragments of ViewPager2
Upvotes: 1
Reputation: 1369
Use getChildFragmentManager()
insead of getSupportFragmentManager()
Upvotes: 3