Home Android & Kotlin Books Android Animations by Tutorials

6
Element Transitions Written by Alex Sullivan

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

You’ve learned about a lot of different types of screen transitions so far, but the coolest is still to come! Motion is the name of the game when building animations, and one of the coolest pieces of motion you can introduce in your apps is the shared element transition. A common place for shared element transition is transitioning from a list item to detail view. The user’s eye can be drawn to certain shared elements in the fragments, instead of transitioning the entire view heirarchy with an enter or exit transition.

In this chapter, you’ll learn:

  • What a shared element transition is.
  • How to use a shared element transition when changing fragments.
  • How to use custom transitions to make your shared element transition beautiful.
  • What a circular reveal animation is and how to use it to reveal tab content.

Now, it’s time to jump right in!

Getting started

Using Android Studio Arctic Fox or newer, open the starter project within 06-element-transitions in the aat-materials repository. Once the project syncs, build and run. You’ll see the login screen.

By the end of this chapter, you’ll have an app full of beautiful, meaningful screen animations!

Introduction to shared element transitions

Shared element transitions are a handy way to share a View between two different Fragments or Activitys. They make it seem as if a View is moving from one screen to another. These shared elements add a sense of continuity between screens.

Anatomy of a shared element transition

Shared element transitions consist of two key components:

Sharing the logo TextView

In the previous chapter, you created a snazzy animation using the Transition framework that showed the Cinematic logo sliding up on the AuthFragment and then back down on the SignupFragment and LoginFragment.

I’r jan Tizs uy Mizububis Giwk ev Fonicugot Yuxoxoqah

Defining a transition name

As a recap, the transition name is a property on a View that the system uses to match up your shared Views. It tells the framework that the two Views are linked and should run a shared element transition between them.

android:transitionName="logo_transition_name"
android:transitionName="logo_transition_name"
android:transitionName="logo_transition_name"

Triggering the shared element transition

In Cinematic, AuthActivity manages the Fragments in the authorization flow. All the Fragments in the authorization flow share the AuthViewModel with AuthActivity. When the user taps a button that should cause AuthActivity to change the current Fragment, the Fragment that’s currently displaying will call a method on AuthViewModel. Calling that method will trigger one of the LiveData objects to either change the Fragment or navigate to the main screen.

viewModel.showLogin.observe(this) {
  showLogin()
}
viewModel.showSignUp.observe(this) {
  showSignup()
}
// 1
val sharedView = findViewById<View>(R.id.logo)
// 2
addSharedElement(sharedView, sharedView.transitionName)
val sharedView = findViewById<View>(R.id.logo)
addSharedElement(sharedView, sharedView.transitionName)

Types of shared element transitions

You’ve set up your shared element transition perfectly, but you still need to tell the framework how exactly you want the animation to run. For example, should the size of the logo View on the auth Fragment screen grow to be the same size as the logo view on the sign-up Fragment screen? Should the logo View rotate as it moves to its final position? There are lots of questions to answer here!

Customizing the shared element transition

For now, you’ll focus on SignupFragment. Later, you’ll port the work you do to the LoginFragment to cover all your bases.

sharedElementEnterTransition = ChangeBounds()

Fixing the fading issue with ChangeTransform

For ChangeTransform to work, you need to use a TransitionSet on your sharedElementEnterTransition. Replace the existing sharedElementEnterTransition with the following:

// 1
val set = TransitionSet()
// 2
val changeBounds = ChangeBounds()
set.addTransition(changeBounds)

val changeTransform = ChangeTransform()
set.addTransition(changeTransform)
// 3
sharedElementEnterTransition = set

Adding an invisible scale to the shared View

To fix the problem, you’ll set a scale on one of the shared Views so there is a scale or rotation change, forcing ChangeTransform to perform the reparenting magic.

android:scaleY="0.99"

Creating a custom text size transition

There’s good news and bad news. The bad news is that Android doesn’t come with a built-in Transition to animate text size. The good news is — you can just make one yourself!

(transitionValues.view as? TextView)?.let { textView ->
  transitionValues.values[textSizeProp] = textView.textSize
}
private fun captureTextSize(transitionValues: TransitionValues) {
  (transitionValues.view as? TextView)?.let { textView ->
    transitionValues.values[TextSizeTransition.textSizeProp] = textView.textSize
  }
}
captureTextSize(transitionValues)
captureTextSize(transitionValues)

Building a text size Animator

Now that you’ve populated the start and end transition values, writing the actual Animator will be a piece of cake. All you need to do is use a ValueAnimator and animate between the two values!

if (startValues == null || endValues == null) {
  return null
}
val startSize = startValues.values[textSizeProp] as Float
val endSize = endValues.values[textSizeProp] as Float
val view = endValues.view as TextView
return ValueAnimator.ofFloat(startSize, endSize).apply {
  addUpdateListener {
    view.setTextSize(TypedValue.COMPLEX_UNIT_PX, it.animatedValue as Float)
  }
}

Wrapping up the logo shared element transition

Now that you’ve created a full-fledged text size transition, it’s time to see it in action.

val textSize = TextSizeTransition()
set.addTransition(textSize)

Revealing a tab with a circular reveal

One of the coolest animations you can trigger in Android is a circular reveal animation. It’s an easy way to show or hide a View with a circular clipping motion, adding some pizzazz to your app.

Xamuyik Vifedare Luhevix Cuvuyabe

Anatomy of a circular reveal

Android exposes a super convenient method, ViewAnimationUtils.createCircularReveal, to create the Animator that does the heavy lifting.

Determining when tab animations should run

As mentioned earlier, you want to build a circular reveal that reveals PopularMoviesFragment and FavoriteMoviesFragment. In contrast to earlier chapters, you won’t use the transition framework or even Fragment or Activity animations. Instead, you’ll trigger the circular reveal from within the Fragment at the right time.

val shouldTriggerFavoriteAnimation = lastBackstackEntry == R.id.popularMoviesFragment &&
    destination.id == R.id.favoriteMoviesFragment
val shouldTriggerPopularAnimation = lastBackstackEntry == R.id.favoriteMoviesFragment &&
    destination.id == R.id.popularMoviesFragment

viewModel.animateFavoriteEntranceLiveData.value = shouldTriggerFavoriteAnimation
viewModel.animatePopularEntranceLiveData.value = shouldTriggerPopularAnimation
animationViewModel.animatePopularEntranceLiveData.observe(viewLifecycleOwner) { shouldAnimate ->
  if (shouldAnimate) {
    animateContentIn()
  }
}

Executing the circular reveal

Navigate to animateContentIn in PopularMoviesFragment. This is where you’ll add the actual circular reveal code. You’ll start by adding a doOnPreDraw block.

binding.root.doOnPreDraw {
}
// 1
val view = binding.root
// 2
val centerX = 0
// 3
val centerY = view.height
val finalRadius = hypot(view.width.toDouble(), view.height.toDouble())
Xesarof Renewoye

val anim = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0f, finalRadius.toFloat())
anim.duration = 600
anim.start()

Creating the circular reveal for the favorite movies screen

All that’s left now is to add very similar code to FavoriteMoviesFragment. Open FavoriteMoviesFragment.kt and replace the body of animateContentIn with the following:

binding.root.doOnPreDraw {
  val view = binding.root
  val centerX = view.width
  val centerY = view.height
  val finalRadius = hypot(view.width.toDouble(), view.height.toDouble())
  val anim = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0f, finalRadius.toFloat())
  anim.duration = 600
  anim.start()
}

Key points

  • Shared element transitions are a wonderful way of transitioning between screens. They improve continuity and add meaningful motion.
  • To use a shared element transition, you need to define the same transition name for the Views in both Fragments.
  • Use setSharedElementTransition to set your shared element transition when changing out Fragments.
  • Use sharedElementEnterTransition to customize your shared element transition’s actual animation.
  • Use ChangeTransform to fix issues where a shared element is caught in another transition.
  • Set a fake scale value if ChangeTransform isn’t executing its reparenting magic.
  • Define custom transitions to do things like animate text size.
  • Create a circular reveal using ViewAnimationUtils.createCircularReveal.
  • Use doOnPreDraw to execute animation code as soon as the View is ready to be drawn.
  • Don’t be afraid of using math to figure out the properties of your animations.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

© 2022 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.