Skip to content

Commit 98f4e62

Browse files
add ViwPager2 to HomeFragment, navigation graphs for ViwPager2 pages
1 parent b2cf969 commit 98f4e62

10 files changed

Lines changed: 263 additions & 22 deletions

File tree

app/src/main/res/values-night/themes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<resources xmlns:tools="http://schemas.android.com/tools">
22
<!-- Base application theme. -->
3-
<style name="Theme.PropertyFindAR" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
3+
<style name="Theme.PropertyFindAR" parent="Theme.MaterialComponents.DayNight.NoActionBar">
44
<!-- Primary brand color. -->
55
<item name="colorPrimary">@color/purple_200</item>
66
<item name="colorPrimaryVariant">@color/purple_700</item>

app/src/main/res/values/themes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<resources xmlns:tools="http://schemas.android.com/tools">
22
<!-- Base application theme. -->
3-
<style name="Theme.PropertyFindAR" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
3+
<style name="Theme.PropertyFindAR" parent="Theme.MaterialComponents.DayNight.NoActionBar">
44
<!-- Primary brand color. -->
55
<item name="colorPrimary">@color/purple_500</item>
66
<item name="colorPrimaryVariant">@color/purple_700</item>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,114 @@
11
package com.smarttoolfactory.home
22

3+
import android.os.Bundle
4+
import android.view.View
5+
import androidx.fragment.app.Fragment
6+
import androidx.fragment.app.activityViewModels
7+
import androidx.lifecycle.Observer
8+
import androidx.navigation.NavController
9+
import androidx.navigation.ui.AppBarConfiguration
10+
import androidx.navigation.ui.setupWithNavController
11+
import androidx.viewpager2.adapter.FragmentStateAdapter
12+
import androidx.viewpager2.widget.ViewPager2
13+
import com.google.android.material.tabs.TabLayout
14+
import com.google.android.material.tabs.TabLayoutMediator
315
import com.smarttoolfactory.core.ui.fragment.DynamicNavigationFragment
16+
import com.smarttoolfactory.core.util.Event
17+
import com.smarttoolfactory.core.viewmodel.NavControllerViewModel
18+
import com.smarttoolfactory.home.adapter.HomeViewPager2FragmentStateAdapter
419
import com.smarttoolfactory.home.databinding.FragmentHomeBinding
520

21+
/**
22+
* Fragment that contains [ViewPager2] and [TabLayout].
23+
* If this fragments get replaced and [Fragment.onDestroyView]
24+
* is called there are things to be considered
25+
*
26+
* * [FragmentStateAdapter] that is not null after [Fragment.onDestroy] cause memory leak,
27+
* so assign null to it
28+
*
29+
* * [TabLayoutMediator] cause memory leak if not detached after [Fragment.onDestroy]
30+
* of this fragment is called.
31+
*
32+
* * Data-binding which is not null after [Fragment.onDestroy] causes memory leak
33+
*
34+
* *[NavControllerViewModel] that has a [NavController] that belong to a NavHostFragment
35+
* that is to be destroyed also causes memory leak.
36+
*/
637
class HomeFragment : DynamicNavigationFragment<FragmentHomeBinding>() {
738

839
override fun getLayoutRes(): Int = R.layout.fragment_home
40+
41+
private val navControllerViewModel by activityViewModels<NavControllerViewModel>()
42+
43+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
44+
super.onViewCreated(view, savedInstanceState)
45+
46+
// ViewPager2
47+
val viewPager = dataBinding!!.viewPager
48+
49+
// TabLayout
50+
val tabLayout = dataBinding!!.tabLayout
51+
52+
/*
53+
Set Adapter for ViewPager inside this fragment using this Fragment,
54+
more specifically childFragmentManager as param
55+
56+
🔥 Create FragmentStateAdapter with viewLifeCycleOwner
57+
https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2
58+
*/
59+
viewPager.adapter =
60+
HomeViewPager2FragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
61+
62+
// Bind tabs and viewpager
63+
TabLayoutMediator(tabLayout, viewPager, tabConfigurationStrategy).attach()
64+
65+
subscribeAppbarNavigation()
66+
}
67+
68+
private fun subscribeAppbarNavigation() {
69+
navControllerViewModel.currentNavController.observe(
70+
viewLifecycleOwner,
71+
Observer { it ->
72+
73+
it?.let { event: Event<NavController?> ->
74+
event.getContentIfNotHandled()?.let { navController ->
75+
val appBarConfig = AppBarConfiguration(navController.graph)
76+
dataBinding!!.toolbar.setupWithNavController(navController, appBarConfig)
77+
}
78+
}
79+
}
80+
)
81+
}
82+
83+
override fun onDestroyView() {
84+
85+
// ViewPager2
86+
val viewPager2 = dataBinding!!.viewPager
87+
// TabLayout
88+
val tabLayout = dataBinding!!.tabLayout
89+
90+
/*
91+
🔥 Detach TabLayoutMediator since it causing memory leaks when it's in a fragment
92+
https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2
93+
*/
94+
TabLayoutMediator(tabLayout, viewPager2, tabConfigurationStrategy).detach()
95+
96+
/*
97+
🔥 Without setting ViewPager2 Adapter to null it causes memory leak
98+
https://stackoverflow.com/questions/62851425/viewpager2-inside-a-fragment-leaks-after-replacing-the-fragment-its-in-by-navig
99+
*/
100+
viewPager2?.let {
101+
it.adapter = null
102+
}
103+
104+
super.onDestroyView()
105+
}
106+
107+
private val tabConfigurationStrategy =
108+
TabLayoutMediator.TabConfigurationStrategy { tab, position ->
109+
when (position) {
110+
0 -> tab.text = "Properties With Flow"
111+
else -> tab.text = "Properties With RxJava3"
112+
}
113+
}
9114
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.smarttoolfactory.home.adapter
2+
3+
import androidx.fragment.app.Fragment
4+
import androidx.fragment.app.FragmentManager
5+
import androidx.lifecycle.Lifecycle
6+
import com.smarttoolfactory.core.ui.adapter.NavigableFragmentStateAdapter
7+
import com.smarttoolfactory.core.ui.fragment.navhost.NavHostContainerFragment
8+
import com.smarttoolfactory.home.R
9+
10+
/**
11+
* FragmentStateAdapter to contain ViewPager2 fragments inside another fragment.
12+
*
13+
* * 🔥 Create FragmentStateAdapter with viewLifeCycleOwner instead of Fragment to make sure
14+
* that it lives between [Fragment.onCreateView] and [Fragment.onDestroyView] while [View] is alive
15+
*
16+
* * https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2
17+
*/
18+
class HomeViewPager2FragmentStateAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
19+
NavigableFragmentStateAdapter(fragmentManager, lifecycle) {
20+
21+
override fun getItemCount(): Int = 3
22+
23+
override fun createFragment(position: Int): Fragment {
24+
25+
return when (position) {
26+
27+
// Fragment with Flow
28+
0 -> NavHostContainerFragment.createNavHostContainerFragment(
29+
R.layout.fragment_navhost_property_list_flow,
30+
R.id.nested_nav_host_fragment_property_list
31+
)
32+
33+
// Fragment with Pagination
34+
else -> NavHostContainerFragment.createNavHostContainerFragment(
35+
R.layout.fragment_navhost_property_list_rxjava3,
36+
R.id.nested_nav_host_fragment_property_list
37+
)
38+
}
39+
}
40+
}
Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,48 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<layout xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto">
4-
<androidx.constraintlayout.widget.ConstraintLayout
4+
5+
<androidx.coordinatorlayout.widget.CoordinatorLayout
56
android:layout_width="match_parent"
67
android:layout_height="match_parent">
78

8-
<com.google.android.material.textview.MaterialTextView
9-
android:id="@+id/tvTitle"
9+
<com.google.android.material.appbar.AppBarLayout
10+
android:id="@+id/appbar"
1011
android:layout_width="match_parent"
1112
android:layout_height="wrap_content"
12-
android:gravity="center"
13-
android:text="HOME\n Under Construction"
14-
android:textSize="30dp"
15-
android:textStyle="bold"
16-
app:layout_constraintBottom_toBottomOf="parent"
17-
app:layout_constraintEnd_toEndOf="parent"
18-
app:layout_constraintStart_toStartOf="parent"
19-
app:layout_constraintTop_toTopOf="parent"
20-
app:layout_constraintVertical_bias="0.25" />
21-
22-
23-
</androidx.constraintlayout.widget.ConstraintLayout>
13+
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
14+
15+
<com.google.android.material.appbar.MaterialToolbar
16+
android:id="@+id/toolbar"
17+
android:layout_width="match_parent"
18+
android:layout_height="?attr/actionBarSize" />
19+
20+
<com.google.android.material.tabs.TabLayout
21+
android:id="@+id/tabLayout"
22+
android:layout_width="match_parent"
23+
android:layout_height="wrap_content"
24+
app:tabMode="scrollable" />
25+
26+
</com.google.android.material.appbar.AppBarLayout>
27+
28+
29+
<androidx.constraintlayout.widget.ConstraintLayout
30+
android:layout_width="match_parent"
31+
android:layout_height="match_parent"
32+
app:layout_behavior="@string/appbar_scrolling_view_behavior">
33+
34+
<androidx.viewpager2.widget.ViewPager2
35+
android:id="@+id/viewPager"
36+
android:layout_width="match_parent"
37+
android:layout_height="match_parent"
38+
app:layout_constraintBottom_toBottomOf="parent"
39+
app:layout_constraintEnd_toEndOf="parent"
40+
app:layout_constraintStart_toStartOf="parent" />
41+
42+
</androidx.constraintlayout.widget.ConstraintLayout>
43+
44+
45+
</androidx.coordinatorlayout.widget.CoordinatorLayout>
46+
47+
2448
</layout>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<layout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto">
4+
5+
<androidx.constraintlayout.widget.ConstraintLayout
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent">
8+
9+
<androidx.fragment.app.FragmentContainerView
10+
android:id="@+id/nested_nav_host_fragment_property_list"
11+
android:name="androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment"
12+
android:layout_width="0dp"
13+
android:layout_height="0dp"
14+
app:defaultNavHost="true"
15+
app:layout_constraintBottom_toBottomOf="parent"
16+
app:layout_constraintLeft_toLeftOf="parent"
17+
app:layout_constraintRight_toRightOf="parent"
18+
app:layout_constraintTop_toTopOf="parent"
19+
app:navGraph="@navigation/nav_graph_property_list_flow" />
20+
21+
</androidx.constraintlayout.widget.ConstraintLayout>
22+
23+
</layout>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<layout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto">
4+
5+
<androidx.constraintlayout.widget.ConstraintLayout
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent">
8+
9+
<androidx.fragment.app.FragmentContainerView
10+
android:id="@+id/nested_nav_host_fragment_property_list"
11+
android:name="androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment"
12+
android:layout_width="0dp"
13+
android:layout_height="0dp"
14+
app:defaultNavHost="true"
15+
app:layout_constraintBottom_toBottomOf="parent"
16+
app:layout_constraintLeft_toLeftOf="parent"
17+
app:layout_constraintRight_toRightOf="parent"
18+
app:layout_constraintTop_toTopOf="parent"
19+
app:navGraph="@navigation/nav_graph_property_list_rxjava3" />
20+
21+
</androidx.constraintlayout.widget.ConstraintLayout>
22+
23+
</layout>

features/home/src/main/res/navigation/nav_graph_home.xml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
xmlns:tools="http://schemas.android.com/tools"
55
android:id="@id/nav_graph_home"
66
app:moduleName="home"
7-
app:startDestination="@id/propertyListFlowFragment">
7+
app:startDestination="@id/homeFragment">
88

99
<fragment
1010
android:id="@+id/homeFragment"
1111
android:name="com.smarttoolfactory.home.HomeFragment"
1212
android:label="HomeFragment"
1313
tools:layout="@layout/fragment_home" />
14-
<fragment
15-
android:id="@+id/propertyListFlowFragment"
16-
android:name="com.smarttoolfactory.home.propertylist.PropertyListFlowFragment"
17-
android:label="PropertyListFlowFragment" />
1814
</navigation>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:id="@+id/nav_graph_property_list"
6+
app:startDestination="@id/propertyListFragment">
7+
8+
<fragment
9+
android:id="@+id/propertyListFragment"
10+
android:name="com.smarttoolfactory.home.propertylist.PropertyListFlowFragment"
11+
android:label="Properties with Flow"
12+
tools:layout="@layout/fragment_property_list">
13+
</fragment>
14+
15+
</navigation>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:id="@+id/nav_graph_property_list"
6+
app:startDestination="@id/propertyListFragment">
7+
8+
<fragment
9+
android:id="@+id/propertyListFragment"
10+
android:name="com.smarttoolfactory.home.propertylist.PropertyListRxjava3Fragment"
11+
android:label="Properties with RxJava3"
12+
tools:layout="@layout/fragment_property_list">
13+
</fragment>
14+
15+
</navigation>

0 commit comments

Comments
 (0)