Shared Element Transitions

Ken Baldauf

Software Engineer @ Acorns

What is it?

  • Seamless screen to screen transition
  • Given views shared by 2 screens
    • Define how they transition between each other
  • Works in parallel with standard enter/exit transitions
  • Requires Android 5.0 (API 21+)
    • Ignored on lower APIs
  • Works with
    • Activity to Activity transitions
    • Fragment to Fragment transitions

Why use them?

  • Gives transitions a sense of continuity between screens
  • Emphasizes views shared between 2 screens
  • Grabs focus of the human eye
    • Directs where the user should look on the new screen
  • Looks better than a generic slide/fade animation

How does it work?

  • Appears to break traditional view boundaries
  • Shared element views aren't truly shared
    • Still operates with 2 distinct views
  • Takes place in window's ViewOverlay
    • This way transition is drawn on top of all other views
  • Transitioning from Screen A to Screen B
    • Screen B measures & lays itself out transparently
    • Compares position of shared views in Screen A & B
    • Screen B draws shared views in Screen A's position
      • Screen A hides its shared views
    • Shared views begin to animate into Screen B's position
      • Screen B gradually fades in onto of Screen A

Types of animations

  • ChangeBounds
    • Animates changes in size & location
  • ChangeTransform
    • Animates scale & rotation changes
  • ChangeClipBounds
    • Animates changes in clip bounds (where view is cutoff)
  • ChangeImageTransform
    • Animate change in ImageView size, shape & ScaleType
  • 4 types can be combined with TransitionSets

Transition example

<style name="AppTheme" parent="Theme.AppCompat.Light">
    <item name="android:windowSharedElementEnterTransition">
        @transition/grid_detail_transition
    </item>
    <item name="android:windowSharedElementReturnTransition">
        @transition/grid_detail_transition
    </item>
</style>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- changeBounds is used for the TextViews which are shared -->
    <changeBounds />

    <!-- changeImageTransform is used for the ImageViews which are shared -->
    <changeImageTransform />

</transitionSet>
// defined in Screen B
val transition = TransitionInflater.from(context)
	.inflateTransition(R.transition.grid_detail_transition)
sharedElementEnterTransition = transition
sharedElementReturnTransition = transition

View example

<!-- Screen B -->
<ImageView
    android:id="@+id/imageview_header"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:transitionName="header:image"
    android:scaleType="centerCrop" />
// Screen B
val headerImage = findViewById(R.id.mageview_header)
ViewCompat.setTransitionName(headerImage, "header:image")
  • Screen A's transitionNames are declared programmatically alongside the view navigation. 
  • transitionNames must be unique

Activity example

// Screen A
val intent: Intent = Intent(this, DetailActivity::class.java)
val activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
	this,
	Pair<View, String>(findViewById(R.id.imageview_item), "header:image"),
	Pair<View, String>(findViewById(R.id.textview_name), "header:title")

ActivityCompat.startActivity(this, intent, activityOptions.toBundle())

Fragment example

// Screen A
val transition = TransitionInflater.from(context)
	.inflateTransition(R.transition.grid_detail_transition)

fragmentTwo.setSharedElementReturnTransition(transition)
fragmentTwo.setSharedElementEnterTransition(transition)

val headerImage = findViewById(R.id.ivProfile)
val headerText = findViewById(R.id.ivProfile)

val ft = fragmentManager.beginTransaction()
	.replace(R.id.container, fragmentTwo)
    	.addToBackStack("transaction")
    	.addSharedElement(headerImage, "header:image")
    	.addSharedElement(headerText, "header:text")
    	.commit()

Dealing with async dependencies

  • postponeEnterTransition()
    • Delays enter animation until data is loaded
    • Called in Activity.onCreate/Fragment.onCreateView
  • startPostponedEnterTransition()
    • Starts previously delayed transition
    • Failure to call will cause the UI to hang
    • Must be called quickly for optimal user experience
  • Should only be used for quick loading async operations
    • ​Good for loading an image with Picasso
    • Bad for most REST/GraphQL calls

Questions?

Shared Element Transitions

By Kenneth Baldauf

Shared Element Transitions

Taking a look at Android shared element transitions. Shared element transitions determine how views that are shared between two activities transition between the activities.

  • 248