I have MainActivity which is implementing Navigation Drawer using the below xml:
<android.support.v4.widget.DrawerLayout
xmlns:android ="http://schemas.android.com/apk/res/android"
android:id ="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/container_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
</LinearLayout>
<FrameLayout
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#FFFFFF"/>
</LinearLayout>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity ="start"
>
<ListView
android:id="@+id/drawerList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:divider="@color/material_blue_grey_800"
android:dividerHeight="1dp"
android:background="#FFFFFF"
/>
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
Now i have 3 items in my listview and on clicking on any one of them my code replace Framelayout with that particular fragment like below:
Fragment f1 = new Fragment()
FragmentTransaction ft = getSupportFragmentManager().beginTransaction()
ft.setCustomAnimations(R.anim.slide_in,R.anim.hyper_out,R.anim.hyper_in,R.anim.slide_out)
ft.replace(R.id.content, f1).addToBackStack(null).commit();
The above code works fine replacing fragment with Custom animations as desired.However, my question is how to animate the toolbar along with fragment during fragment transactions.
All the fragments have their respective toolbar titles which are changed in onActivityCreated() method of each Fragment Class by following code:
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("Title");
Should i be applying animations to my layouts to cover up the toolbar?
At a high level, here's how to make a fragment transition with shared elements: Assign a unique transition name to each shared element view. Add shared element views and transition names to the FragmentTransaction . Set a shared element transition animation.
To animate the transition between fragments, or to animate the process of showing or hiding a fragment you use the Fragment Manager to create a Fragment Transaction . Within each Fragment Transaction you can specify in and out animations that will be used for show and hide respectively (or both when replace is used).
Use replace() to replace an existing fragment in a container with an instance of a new fragment class that you provide. Calling replace() is equivalent to calling remove() with a fragment in a container and adding a new fragment to that same container. transaction. commit();
I had this same question - it seems to pop up at some point in the development process of every app, and I never solve it, and revert back to some subpar solution. Now I'm back with a solution!
Answer in Kotlin, but the same principles apply in Java.
To animate the Toolbar
alongside the FragmentTransaction
animations, you first need a way to inform the Activity/Fragment
holding the Toolbar
that the animation is starting/ending. I'll call the Activity/Fragment
holding the Toolbar
the ToolbarHost
First, define an interface which the ToolbarHost
can implement, to receive callbacks:
interface FragmentAnimationListener {
fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean)
fun onAnimationEnd(fragment: Fragment, animation: Animation, enter: Boolean)
}
I've also added an extension method to help the child Fragment
find the parent ToolbarHost
interested in receiving the callbacks:
fun Fragment.findParentAnimationListener(): FragmentAnimationListener? {
return when (parentFragment) {
is FragmentAnimationListener -> parentFragment as FragmentAnimationListener
null -> return activity as? FragmentAnimationListener
else -> parentFragment?.findParentAnimationListener()
}
}
Now we add the following to the child Fragment
which is about to be animated onto the screen. I suggest adding the following to a base Fragment
class. This intercepts the animation supplied to the child Fragment
from the FragmentTransaction
*, adds a listener, finds ToolbarHost
, and notifies it of our animation starting/ending.
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
var animation = super.onCreateAnimation(transit, enter, nextAnim)
if (animation == null && nextAnim != 0) {
animation = AnimationUtils.loadAnimation(activity!!, nextAnim)
}
val parentAnimationListener = findParentAnimationListener()
if (animation != null) {
animation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
parentAnimationListener?.onAnimationStart(this@BaseFragment, animation, enter)
}
override fun onAnimationEnd(animation: Animation) {
parentAnimationListener?.onAnimationEnd(this@BaseFragment, animation, enter)
}
override fun onAnimationRepeat(animation: Animation?) {
}
})
}
return animation
}
* This works for animations supplied via FragmentTransaction.setCustomAnimation(int, int, ..)
. I haven't tested this with different types of FragmentTransaction
animations, or no FragmentTransaction
animation.
Now that our ToolbarHost
knows when the child Fragment
is performing its animations, we can think about animating the Toolbar alongside.
Assuming your ToolbarHost
is a fragment, we first implement FragmentAnimationListener
:
class ParentFragment : Fragment(), FragmentAnimationListener {
override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
...
}
override fun onAnimationEnd(fragment: Fragment, animation: Animation, enter: Boolean) {
...
}
}
Now, one issue we have right off the bat, is since we added our AnimationListener
to a base Fragment
class, our onAnimationStart
and onAnimationEnd
are going to be called from both the fragment that is exiting, and the fragment that is entering (assuming your FragmentTransaction
is replacing one fragment with another). So before we try and do anything with the Toolbar
, we need to filter out only the fragment we're interested in.
I'm going to use the 'entering' Fragment
as the trigger for Toolbar animations - and when we pop the stack, I'm going to use the same Fragment
(now 'exiting') again. So, the only Fragment
I'm interested in is the one being transitioned to:
class ParentFragment : Fragment(), FragmentAnimationListener {
var newFragment: Fragment? = null
fun replaceFragment(newFragment: Fragment) {
// Store a reference to the fragment we're transitioning to
this.newFragment = newFragment
childFragmentManager.beginTransaction()
.setCustomAnimations(..)
.replace(container, newFragment, tag)
.commit()
}
override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
if (fragment == newFragment) {
if (enter) {
// Animate our Toolbar in
} else {
// Animate our Toolbar out
}
}
}
}
The last step is to actually perform the Toolbar animation. You can create your own Animation
, with your own timing, interpolator, etc - but I prefer to just use the same Animator
that's animating the child Fragment
. That way, if we're fading in, the Toolbar
will fade in. If we're sliding out, the Toolbar
will slide out. By using the same Animation
we can be certain we have the correct duration and interpolator, so our Animations
will run at the same time.
override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
if (fragment == newFragment) {
toolbar.animation = animation
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With