Share
Embed
Animation To Guide Us All 2nd Edition
At DevFest Seattle and Android TO I talked about how animations can greatly increase the UX of your app and make it more simple.
The source code for the project can be found here: https://github.com/marcospaulo/cinema_example
Marcos Paulo Souza Damasceno
October 15, 2016
Transcript
87 slides
-
Animations to Guide Us All How animations can help guide
your users +Marcos Damasceno @marcospaulosd -
+Marcos Damasceno @marcospaulosd Google Developer Expert Android Android Engineer @
Mirego Droidcon Montreal and GDG Android Montreal Organizer Speaker (Droidcon NYC, London…) Brazilian -
Outlines Evolution of Mobile Apps UX Complexities Meaningful Animations Activity
Transitions Conclusion -
The Evolution of Mobile Apps At the beginning of all
times, Mobile Applications were there for simple uses. -
The Evolution of Mobile Apps Big screens have brought way
more possibilities But bad design strategies decrease UX quality -
The UX/UI Dilema Unnecessary Buttons Lack of context Bad use
of design strategies -
The UX/UI Dilema Coding an app is not the hardest
part anymore, but actually knowing where to put your elements, functionalities and even how to do the flow of your app is the real challenge. -
Motion Design Where things come from, where do they go,
what can I do. Motion Matters -
Drawables Overview We will see how we can take advantage
of drawables so we can reduce the amount of resources in our application. Also we will see cases where a drawable could easily replace views, making our app more clean and faster. Objective -
Motion Design Animations to the rescue Animations can help simplify
your app and eliminate unnecessary tutorials or even buttons -
Motion Design Just answer these 3 questions Where things come
from Where do they go What can I do -
Motion Design Your animations should make sense It should have
a reason to exist -
Activity Transitions Activity transitions in material design apps provide visual
connections between different states through motion and transformations between common elements -
Activity Transitions Enter Exit Shared Element How views in an
activity enter the scene How views in an activity exit the scene How views shared between 2 activities transition -
Animations should make sense Choreography should be used to show
the elements of your screen to the user, guiding him so he can know what he can do -
rvMovies.setAdapter(new MoviesAdapter(Movie.createMovies(), new MoviesAdapter.OnMovieClickListener() { @Override public void onMovieClick(Movie movie,
ImageView ivMovieCover) { Intent intent = MovieDetailActivity.newIntent(MainActivity.this, movie); ivMovieCover.setTransitionName("movie_cover"); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, ivMovieCover, "movie_cover").toBundle()); } })); We need access to the element that will be shared MoviesActivity.java -
rvMovies.setAdapter(new MoviesAdapter(Movie.createMovies(), new MoviesAdapter.OnMovieClickListener() { @Override public void onMovieClick(Movie movie,
ImageView ivMovieCover) { Intent intent = MovieDetailActivity.newIntent(MainActivity.this, movie); ivMovieCover.setTransitionName("movie_cover"); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, ivMovieCover, "movie_cover").toBundle()); } })); Set Transition Name MoviesActivity.java -
rvMovies.setAdapter(new MoviesAdapter(Movie.createMovies(), new MoviesAdapter.OnMovieClickListener() { @Override public void onMovieClick(Movie movie,
ImageView ivMovieCover) { Intent intent = MovieDetailActivity.newIntent(MainActivity.this, movie); ivMovieCover.setTransitionName("movie_cover"); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, ivMovieCover, "movie_cover").toBundle()); } })); Pass view and transition name MoviesActivity.java -
@Override public void onCreate(Bundle savedInstanceState) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
super.onCreate(savedInstanceState); setContentView(R.layout.activity_movie_detail); ButterKnife.bind(this); movie = (Movie) getIntent().getSerializableExtra(EXTRA_MOVIE); toolbar.setBackgroundResource(movie.resImageBanner); ivMovieCover.setImageResource(movie.resImageCover); tvMovieTitle.setText(movie.name); tvMovieSubtitle.setText(movie.subtitle); tvMovieDescription.setText(movie.description); ivMovieCover.setTransitionName("movie_cover"); } Set transition name in the next activity MovieDetailActivity.java -
@Override public void onCreate(Bundle savedInstanceState) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
super.onCreate(savedInstanceState); setContentView(R.layout.activity_movie_detail); ButterKnife.bind(this); movie = (Movie) getIntent().getSerializableExtra(EXTRA_MOVIE); toolbar.setBackgroundResource(movie.resImageBanner); ivMovieCover.setImageResource(movie.resImageCover); tvMovieTitle.setText(movie.name); tvMovieSubtitle.setText(movie.subtitle); tvMovieDescription.setText(movie.description); ivMovieCover.setTransitionName("movie_cover"); } Set transition name in the next activity MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
Activity Transitions are easy they said It will save you
time they said -
The problem /** @hide */ @Override public void captureTransitioningViews(List<View> transitioningViews)
{ if (getVisibility() != View.VISIBLE) { return; } if (isTransitionGroup()) { transitioningViews.add(this); } else { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.captureTransitioningViews(transitioningViews); } } } Before the transition starts, the framework constructs the set of transitioning views by performing a recursive search on the Activity window’s (or Fragment’s) entire view hierarchy. -
The problem /** @hide */ @Override public void captureTransitioningViews(List<View> transitioningViews)
{ if (getVisibility() != View.VISIBLE) { return; } if (isTransitionGroup()) { transitioningViews.add(this); } else { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.captureTransitioningViews(transitioningViews); } } } Before the transition starts, the framework constructs the set of transitioning views by performing a recursive search on the Activity window’s (or Fragment’s) entire view hierarchy. -
The problem /** @hide */ @Override public void captureTransitioningViews(List<View> transitioningViews)
{ if (getVisibility() != View.VISIBLE) { return; } if (isTransitionGroup()) { transitioningViews.add(this); } else { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.captureTransitioningViews(transitioningViews); } } } Before the transition starts, the framework constructs the set of transitioning views by performing a recursive search on the Activity window’s (or Fragment’s) entire view hierarchy. -
The problem /** @hide */ @Override public void captureTransitioningViews(List<View> transitioningViews)
{ if (getVisibility() != View.VISIBLE) { return; } if (isTransitionGroup()) { transitioningViews.add(this); } else { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.captureTransitioningViews(transitioningViews); } } } Before the transition starts, the framework constructs the set of transitioning views by performing a recursive search on the Activity window’s (or Fragment’s) entire view hierarchy. -
The problem <android.support.v7.widget.CardView android:id="@+id/card_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/toolbar" android:elevation="0dp" app:cardElevation="8dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="128dp" android:paddingLeft="128dp" android:orientation="vertical"> <TextView android:id="@+id/tv_movie_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" tools:text="Batman v Superman: Dawn of Justice" android:textColor="@android:color/black" android:textSize="20dp" /> <TextView android:id="@+id/tv_movie_subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="June 2013 PG 142 minutes" /> -
Add Exit and Return transition private void makeExitTransition(){ cardInfo.setTransitionGroup(true);
Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar); Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.card_info); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromBottom); transitionSet.addTransition(slideFromUp); getWindow().setExitTransition(transitionSet); getWindow().setReturnTransition(transitionSet); } MovieDetailActivity.java -
Add Exit and Return transition private void makeExitTransition(){ cardInfo.setTransitionGroup(true);
Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar); Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.card_info); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromBottom); transitionSet.addTransition(slideFromUp); getWindow().setExitTransition(transitionSet); getWindow().setReturnTransition(transitionSet); } MovieDetailActivity.java -
Add Exit and Return transition private void makeExitTransition(){ cardInfo.setTransitionGroup(true);
Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar); Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.card_info); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromBottom); transitionSet.addTransition(slideFromUp); getWindow().setExitTransition(transitionSet); getWindow().setReturnTransition(transitionSet); } MovieDetailActivity.java -
Add Exit and Return transition private void makeExitTransition(){ cardInfo.setTransitionGroup(true);
Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar); Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.card_info); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromBottom); transitionSet.addTransition(slideFromUp); getWindow().setExitTransition(transitionSet); getWindow().setReturnTransition(transitionSet); } MovieDetailActivity.java -
Add Exit and Return transition @Override public void onBackPressed() {
makeExitTransition(); super.onBackPressed(); } MovieDetailActivity.java -
public void makeEnterTransition(){ Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(slideFromBottom); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Defining different transitions MovieDetailActivity.java -
Let’s make it better private void makeEnterTransition() { Slide slideFromUp
= new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar); Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.setDuration(500); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); Fade fade = new Fade(); fade.setDuration(1000); fade.addTarget(R.id.tv_movie_description); fade.addTarget(R.id.tv_movie_subtitle); fade.addTarget(R.id.tv_movie_title); TransitionSet transitionSetInfo = new TransitionSet(); transitionSetInfo.addTransition(slideFromBottom); transitionSetInfo.addTransition(fade); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(transitionSetInfo); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } MovieDetailActivity.java -
private void makeEnterTransition() { Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.setDuration(500); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); Fade fade = new Fade(); fade.setDuration(1000); fade.addTarget(R.id.tv_movie_description); fade.addTarget(R.id.tv_movie_subtitle); fade.addTarget(R.id.tv_movie_title); TransitionSet transitionSetInfo = new TransitionSet(); transitionSetInfo.addTransition(slideFromBottom); transitionSetInfo.addTransition(fade); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(transitionSetInfo); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Let’s make it better MovieDetailActivity.java -
private void makeEnterTransition() { Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.setDuration(500); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); Fade fade = new Fade(); fade.setDuration(1000); fade.addTarget(R.id.tv_movie_description); fade.addTarget(R.id.tv_movie_subtitle); fade.addTarget(R.id.tv_movie_title); TransitionSet transitionSetInfo = new TransitionSet(); transitionSetInfo.addTransition(slideFromBottom); transitionSetInfo.addTransition(fade); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(transitionSetInfo); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } MovieDetailActivity.java Let’s make it better -
private void makeEnterTransition() { Slide slideFromUp = new Slide(Gravity.TOP); slideFromUp.addTarget(R.id.toolbar);
Slide slideFromBottom = new Slide(Gravity.BOTTOM); slideFromBottom.setDuration(500); slideFromBottom.addTarget(R.id.tv_movie_description); slideFromBottom.addTarget(R.id.tv_movie_subtitle); slideFromBottom.addTarget(R.id.tv_movie_title); Fade fade = new Fade(); fade.setDuration(1000); fade.addTarget(R.id.tv_movie_description); fade.addTarget(R.id.tv_movie_subtitle); fade.addTarget(R.id.tv_movie_title); TransitionSet transitionSetInfo = new TransitionSet(); transitionSetInfo.addTransition(slideFromBottom); transitionSetInfo.addTransition(fade); TransitionSet transitionSet = new TransitionSet(); transitionSet.addTransition(slideFromUp); transitionSet.addTransition(transitionSetInfo); transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); getWindow().setEnterTransition(transitionSet); } Let’s make it better MovieDetailActivity.java -
Add reveal for movie banner MovieDetailActivity.java toolbar.setVisibility(View.INVISIBLE); setEnterSharedElementCallback(new SharedElementCallback() {
@Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { final Animator animator = ViewAnimationUtils.createCircularReveal(toolbar, cardInfo.getRight() / 2, cardInfo.getTop() / 2, 0, cardInfo.getWidth() + cardInfo.getHeight()); Handler handler = new Handler(); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(600); handler.postDelayed(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); animator.start(); } }, 300); } }); -
Add reveal for movie banner MovieDetailActivity.java toolbar.setVisibility(View.INVISIBLE); setEnterSharedElementCallback(new SharedElementCallback() {
@Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { final Animator animator = ViewAnimationUtils.createCircularReveal(toolbar, cardInfo.getRight() / 2, cardInfo.getTop() / 2, 0, cardInfo.getWidth() + cardInfo.getHeight()); Handler handler = new Handler(); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(600); handler.postDelayed(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); animator.start(); } }, 300); } }); -
Add reveal for movie banner MovieDetailActivity.java toolbar.setVisibility(View.INVISIBLE); setEnterSharedElementCallback(new SharedElementCallback() {
@Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { final Animator animator = ViewAnimationUtils.createCircularReveal(toolbar, cardInfo.getRight() / 2, cardInfo.getTop() / 2, 0, cardInfo.getWidth() + cardInfo.getHeight()); Handler handler = new Handler(); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(600); handler.postDelayed(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); animator.start(); } }, 300); } }); -
Add reveal for movie banner MovieDetailActivity.java toolbar.setVisibility(View.INVISIBLE); setEnterSharedElementCallback(new SharedElementCallback() {
@Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { final Animator animator = ViewAnimationUtils.createCircularReveal(toolbar, cardInfo.getRight() / 2, cardInfo.getTop() / 2, 0, cardInfo.getWidth() + cardInfo.getHeight()); Handler handler = new Handler(); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(600); handler.postDelayed(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); animator.start(); } }, 300); } }); -
Add reveal for movie banner MovieDetailActivity.java toolbar.setVisibility(View.INVISIBLE); setEnterSharedElementCallback(new SharedElementCallback() {
@Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { final Animator animator = ViewAnimationUtils.createCircularReveal(toolbar, cardInfo.getRight() / 2, cardInfo.getTop() / 2, 0, cardInfo.getWidth() + cardInfo.getHeight()); Handler handler = new Handler(); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(600); handler.postDelayed(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); animator.start(); } }, 300); } }); -
Activity Transitions - Easy to use once understand how it
works - Limited customization due to TransitioGroup handling - Only available on Lollipop + -
THINK LIKE AN ANIMATOR The way you write your xml
layout will directly affect how your Activity Transition Animations will work.