Skip to content

Commit 7516390

Browse files
Piotr ZawadzkiPiotr Zawadzki
authored andcommitted
Added transitions between Fragment i.e. content fade with Fragment's Toolbars not being animated.
1 parent 4803ede commit 7516390

13 files changed

Lines changed: 54 additions & 22 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ This is a sample project where I experimented with different aspects of the [And
99
- custom handling of implicit deep links (more on that later)
1010
- passing arguments between Fragments via [Safe Args](https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args)
1111
- starting Fragments for result and waiting for that result (similar to Activity's `startActivityForResult` and `onActivityResult`)
12+
- transitions between fragments with shared Toolbar
1213

1314
There's no MVP/MVVM/MVI here, no dependency injection - just the Navigation Component to make it simple.
1415

1516
## How does it look like?
1617

1718
### App
18-
<img src ="./art/playground_app.gif" width="360" />
19+
<img src ="./art/navigation_playground_app.gif" width="360" />
1920

2021
### Graph
2122
<img src ="./art/nav_graph.png" width="640" />

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/ApplyFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ApplyFragment : BaseFragment() {
1111
inflater: LayoutInflater, container: ViewGroup?,
1212
savedInstanceState: Bundle?
1313
): View? {
14-
// Inflate the layout for this fragment
14+
initEnterTransitions()
1515
return inflater.inflate(R.layout.fragment_apply, container, false)
1616
}
1717
}

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/BaseFragment.kt

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package com.github.zawadz88.navigationcomponentplayground
22

33
import android.os.Bundle
4+
import android.transition.ChangeBounds
5+
import android.transition.Fade
46
import androidx.annotation.CallSuper
57
import androidx.annotation.IdRes
68
import androidx.fragment.app.Fragment
79
import androidx.fragment.app.FragmentManager
810
import androidx.navigation.NavDirections
911
import androidx.navigation.NavOptions
1012
import androidx.navigation.Navigator
13+
import androidx.navigation.fragment.FragmentNavigatorExtras
1114
import androidx.navigation.fragment.findNavController
1215
import com.github.zawadz88.navigationcomponentplayground.navigation.BackNavigationListener
1316
import com.github.zawadz88.navigationcomponentplayground.navigation.BackNavigationResult
17+
import kotlinx.android.synthetic.main.appbar.appBarLayout
1418
import kotlinx.android.synthetic.main.appbar.toolbar
1519
import timber.log.Timber
1620
import kotlin.properties.Delegates
@@ -30,6 +34,8 @@ abstract class BaseFragment : Fragment() {
3034
private val requestCode: Int
3135
get() = arguments?.getInt(ARGUMENT_NAVIGATION_REQUEST_CODE, REQUEST_CODE_NOT_SET) ?: REQUEST_CODE_NOT_SET
3236

37+
private val appBarTransition: String by lazy { requireActivity().getString(R.string.transition_name_appbar) }
38+
3339
override fun onCreate(savedInstanceState: Bundle?) {
3440
super.onCreate(savedInstanceState)
3541
Timber.d("onCreate: ${this::class.java.simpleName}")
@@ -63,7 +69,21 @@ abstract class BaseFragment : Fragment() {
6369
}
6470
}
6571

66-
fun navigateForResult(
72+
protected fun navigateForResultWithAnimation(
73+
requestCode: Int, navDirections: NavDirections, navOptions: NavOptions? = null
74+
) {
75+
val extras = FragmentNavigatorExtras(appBarLayout to appBarTransition)
76+
this.exitTransition = Fade()
77+
navigateForResult(
78+
resId = navDirections.actionId,
79+
requestCode = requestCode,
80+
args = navDirections.arguments,
81+
navOptions = navOptions,
82+
navigatorExtras = extras
83+
)
84+
}
85+
86+
protected fun navigateForResult(
6787
requestCode: Int, navDirections: NavDirections, navOptions: NavOptions? = null,
6888
navigatorExtras: Navigator.Extras? = null
6989
) =
@@ -75,7 +95,7 @@ abstract class BaseFragment : Fragment() {
7595
navigatorExtras = navigatorExtras
7696
)
7797

78-
fun navigateForResult(
98+
protected fun navigateForResult(
7999
@IdRes resId: Int, requestCode: Int, args: Bundle? = null, navOptions: NavOptions? = null,
80100
navigatorExtras: Navigator.Extras? = null
81101
) {
@@ -85,6 +105,12 @@ abstract class BaseFragment : Fragment() {
85105
findNavController().navigate(resId, argsWithRequestCode, navOptions, navigatorExtras)
86106
}
87107

108+
protected fun navigateForwardWithAnimation(navDirections: NavDirections) {
109+
val extras = FragmentNavigatorExtras(appBarLayout to appBarTransition)
110+
this.exitTransition = Fade()
111+
findNavController().navigate(navDirections, extras)
112+
}
113+
88114
protected fun navigateBackWithResult(resultCode: Int, data: Bundle? = null): Boolean =
89115
navigateBackWithResult(DESTINATION_NOT_SET, BackNavigationResult(requestCode, resultCode, data))
90116

@@ -93,6 +119,11 @@ abstract class BaseFragment : Fragment() {
93119

94120
protected fun navigateUp(): Boolean = findNavController().navigateUp()
95121

122+
protected fun initEnterTransitions() {
123+
sharedElementEnterTransition = ChangeBounds()
124+
enterTransition = Fade()
125+
}
126+
96127
private fun navigateBackWithResult(@IdRes destination: Int, result: BackNavigationResult): Boolean {
97128
val childFragmentManager = requireActivity().supportFragmentManager.findFragmentById(R.id.nav_host_fragment)?.childFragmentManager
98129
var backStackListener: FragmentManager.OnBackStackChangedListener by Delegates.notNull()

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/HomeFragment.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import android.view.ViewGroup
1111
import androidx.core.app.NotificationCompat
1212
import androidx.core.content.ContextCompat
1313
import androidx.navigation.NavDeepLinkBuilder
14-
import androidx.navigation.fragment.findNavController
14+
import com.github.zawadz88.navigationcomponentplayground.HomeFragmentDirections.Companion.actionHomeFragmentToLoginNoPasswordFragment
15+
import com.github.zawadz88.navigationcomponentplayground.HomeFragmentDirections.Companion.actionHomeFragmentToLoginWithPasswordFragment
16+
import com.github.zawadz88.navigationcomponentplayground.HomeFragmentDirections.Companion.actionHomeFragmentToOfferFragment
1517
import kotlinx.android.synthetic.main.fragment_home.fragmentLoginButton
1618
import kotlinx.android.synthetic.main.fragment_home.fragmentLoginWithPasswordButton
1719
import kotlinx.android.synthetic.main.fragment_home.fragmentOfferButton
@@ -36,13 +38,13 @@ class HomeFragment : BaseFragment() {
3638
override fun onActivityCreated(savedInstanceState: Bundle?) {
3739
super.onActivityCreated(savedInstanceState)
3840
fragmentLoginButton.setOnClickListener {
39-
findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToLoginNoPasswordFragment())
41+
navigateForwardWithAnimation(actionHomeFragmentToLoginNoPasswordFragment())
4042
}
4143
fragmentLoginWithPasswordButton.setOnClickListener {
42-
findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToLoginWithPasswordFragment())
44+
navigateForwardWithAnimation(actionHomeFragmentToLoginWithPasswordFragment())
4345
}
4446
fragmentOfferButton.setOnClickListener {
45-
findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToOfferFragment(1234))
47+
navigateForwardWithAnimation(actionHomeFragmentToOfferFragment(1234))
4648
}
4749
sendNotificationButton.setOnClickListener { prepareAndSendNotification() }
4850
}

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/OfferFragment.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.view.LayoutInflater
55
import android.view.View
66
import android.view.ViewGroup
77
import android.widget.Toast
8-
import androidx.navigation.fragment.findNavController
98
import androidx.navigation.fragment.navArgs
109
import com.github.zawadz88.navigationcomponentplayground.login.NAVIGATION_RESULT_LOGGED_IN
1110
import com.github.zawadz88.navigationcomponentplayground.navigation.BackNavigationListener
@@ -27,7 +26,7 @@ class OfferFragment : BackNavigationListener, BaseFragment() {
2726
inflater: LayoutInflater, container: ViewGroup?,
2827
savedInstanceState: Bundle?
2928
): View? {
30-
// Inflate the layout for this fragment
29+
initEnterTransitions()
3130
return inflater.inflate(R.layout.fragment_offer, container, false)
3231
}
3332

@@ -36,10 +35,10 @@ class OfferFragment : BackNavigationListener, BaseFragment() {
3635
Toast.makeText(requireContext(), "myId: $myId", Toast.LENGTH_SHORT).show()
3736
fragmentApplyButton.setOnClickListener { goToApply() }
3837
fragmentLoginWithPasswordButton.setOnClickListener {
39-
navigateForResult(REQUEST_CODE_LOGIN, OfferFragmentDirections.actionOfferFragmentToLoginWithPasswordFragment())
38+
navigateForResultWithAnimation(REQUEST_CODE_LOGIN, OfferFragmentDirections.actionOfferFragmentToLoginWithPasswordFragment())
4039
}
4140
fragmentLoginButton.setOnClickListener {
42-
navigateForResult(REQUEST_CODE_LOGIN, OfferFragmentDirections.actionOfferFragmentToLoginNoPaswordFragment())
41+
navigateForResultWithAnimation(REQUEST_CODE_LOGIN, OfferFragmentDirections.actionOfferFragmentToLoginNoPaswordFragment())
4342
}
4443
}
4544

@@ -53,6 +52,6 @@ class OfferFragment : BackNavigationListener, BaseFragment() {
5352
}
5453

5554
private fun goToApply() {
56-
findNavController().navigate(OfferFragmentDirections.actionOfferFragmentToApplyFragment())
55+
navigateForwardWithAnimation(OfferFragmentDirections.actionOfferFragmentToApplyFragment())
5756
}
5857
}

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/login/DeepLinkLoginFragment.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ class DeepLinkLoginFragment : BaseFragment() {
1717
override fun onCreateView(
1818
inflater: LayoutInflater, container: ViewGroup?,
1919
savedInstanceState: Bundle?
20-
): View? {
21-
// Inflate the layout for this fragment
22-
return inflater.inflate(R.layout.fragment_deep_link_login, container, false)
23-
}
20+
): View? = inflater.inflate(R.layout.fragment_deep_link_login, container, false)
2421

2522
override fun onActivityCreated(savedInstanceState: Bundle?) {
2623
super.onActivityCreated(savedInstanceState)

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/login/LoginNoPasswordFragment.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class LoginNoPasswordFragment : BaseFragment(), BackNavigationListener {
2121
inflater: LayoutInflater, container: ViewGroup?,
2222
savedInstanceState: Bundle?
2323
): View? {
24-
// Inflate the layout for this fragment
24+
initEnterTransitions()
2525
return inflater.inflate(R.layout.fragment_login_no_password, container, false)
2626
}
2727

@@ -31,7 +31,7 @@ class LoginNoPasswordFragment : BaseFragment(), BackNavigationListener {
3131
navigateBackWithResult(NAVIGATION_RESULT_OK)
3232
}
3333
fragmentLoginWithPasswordButton.setOnClickListener {
34-
navigateForResult(REQUEST_CODE_COLLECTIVE_LOGIN, LoginNoPasswordFragmentDirections.actionLoginNoPasswordFragmentToLoginWithPasswordFragment())
34+
navigateForResultWithAnimation(REQUEST_CODE_COLLECTIVE_LOGIN, LoginNoPasswordFragmentDirections.actionLoginNoPasswordFragmentToLoginWithPasswordFragment())
3535
}
3636
}
3737

app/src/main/java/com/github/zawadz88/navigationcomponentplayground/login/LoginWithPasswordFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class LoginWithPasswordFragment : BaseFragment() {
1515
inflater: LayoutInflater, container: ViewGroup?,
1616
savedInstanceState: Bundle?
1717
): View? {
18-
// Inflate the layout for this fragment
18+
initEnterTransitions()
1919
return inflater.inflate(R.layout.fragment_login_with_password, container, false)
2020
}
2121

app/src/main/res/layout/appbar.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
xmlns:app="http://schemas.android.com/apk/res-auto">
44

55
<com.google.android.material.appbar.AppBarLayout
6+
android:id="@+id/appBarLayout"
7+
android:transitionName="@string/transition_name_appbar"
68
android:layout_width="match_parent"
79
android:layout_height="wrap_content"
810
android:theme="@style/AppTheme.AppBarOverlay">

app/src/main/res/navigation/nav_graph.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
app:destination="@id/loginNoPasswordFragment" />
1919
<action
2020
android:id="@+id/action_homeFragment_to_loginWithPasswordFragment"
21-
app:destination="@id/loginWithPasswordFragment"
22-
app:launchSingleTop="false" />
21+
app:destination="@id/loginWithPasswordFragment" />
2322
</fragment>
2423
<fragment
2524
android:id="@+id/loginWithPasswordFragment"

0 commit comments

Comments
 (0)