How to animate floating action button using android activity transition? How to animate floating action button using android activity transition? android android

How to animate floating action button using android activity transition?


Question is old, however it is still interesting. That's how I implemented this:

enter image description here

First of all you need to create two ViewGroups which in transition api are called "scenes". First scene contains views before transition, second one contain views after transition. Then you should just replace first scene with second one and provide Transition which describes how views from first scene goes to second scene, also how views in first scene disappear and how views in second scene appear.

enter image description here

In this example there is difficulty with fab button, because it is the only view which is animated out of white area of "scene root" screen. Thats why scenes are fullscreen with margin top which equals to height of blue "monday" header.

All transitions here are default except of yellow background transition. Yellow background view appears in second scene with circular reveal animation. And it dissapear with circular reveal collapse animation. There is no default animation like this, thats why I wrote custom one:

import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.support.transition.TransitionValues;import android.support.transition.Visibility;import android.view.View;import android.view.ViewAnimationUtils;import android.view.ViewGroup;public class CircularRevealTransition extends Visibility {@Overridepublic Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {    int startRadius = 0;    int endRadius = (int) Math.hypot(view.getWidth(), view.getHeight());    Animator reveal = ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2, view.getHeight() / 2, startRadius, endRadius);    //make view invisible until animation actually starts    view.setAlpha(0);    reveal.addListener(new AnimatorListenerAdapter() {        @Override        public void onAnimationStart(Animator animation) {            view.setAlpha(1);        }    });    return reveal;}@Overridepublic Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {    int endRadius = 0;    int startRadius = (int) Math.hypot(view.getWidth(), view.getHeight());    Animator reveal = ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2, view.getHeight() / 2, startRadius, endRadius);    return reveal;}}

Full code is here:

import android.os.Bundle;import android.support.annotation.NonNull;import android.support.design.widget.FloatingActionButton;import android.support.transition.ArcMotion;import android.support.transition.ChangeBounds;import android.support.transition.Fade;import android.support.transition.Scene;import android.support.transition.Slide;import android.support.transition.Transition;import android.support.transition.TransitionListenerAdapter;import android.support.transition.TransitionManager;import android.support.transition.TransitionSet;import android.support.v7.app.AppCompatActivity;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;public class MainActivity extends AppCompatActivity {    private ViewGroup mSceneRoot;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        setSupportActionBar(findViewById(R.id.toolbar));        mSceneRoot = findViewById(R.id.sceneRoot);        //show scene1 without animation        showScene1(false);    }    private void showScene1(boolean animated) {        ViewGroup root = (ViewGroup) getLayoutInflater().inflate(R.layout.scene1, null);        FloatingActionButton fab = root.findViewById(R.id.fab);        fab.setOnClickListener(v -> {            showScene2();        });        Scene scene = new Scene(mSceneRoot, root);        Transition transition = animated ? getScene1Transition() : null;        TransitionManager.go(scene, transition);    }    private void showScene2() {        ViewGroup root = (ViewGroup) getLayoutInflater().inflate(R.layout.scene2, null);        View btnBack = root.findViewById(R.id.btnCancel);        btnBack.setOnClickListener(v -> {            showScene1(true);        });        Scene scene = new Scene(mSceneRoot, root);        Transition transition = getScene2Transition();        TransitionManager.go(scene, transition);    }    private Transition getScene2Transition() {        TransitionSet set = new TransitionSet();        //fab changes position        ChangeBounds changeTransform = new ChangeBounds();        changeTransform.addListener(new TransitionListenerAdapter() {            @Override            public void onTransitionEnd(@NonNull Transition transition) {                //hide fab button on the end of animation                mSceneRoot.findViewById(R.id.fab).setVisibility(View.INVISIBLE);            }        });        changeTransform.addTarget(R.id.fab);        changeTransform.setDuration(300);        //fab arc path        ArcMotion arcMotion = new ArcMotion();        arcMotion.setMaximumAngle(45);        arcMotion.setMinimumHorizontalAngle(90);        arcMotion.setMinimumVerticalAngle(0);        changeTransform.setPathMotion(arcMotion);        set.addTransition(changeTransform);        //bg circular reveal animation starts        CircularRevealTransition crt = new CircularRevealTransition();        crt.addTarget(R.id.yellowBG);        crt.setStartDelay(200);        crt.setDuration(600);        set.addTransition(crt);        //buttons appear        Fade fade = new Fade();        fade.addTarget(R.id.btnBegin);        fade.addTarget(R.id.btnCancel);        fade.addTarget(R.id.text);        fade.setStartDelay(600);        set.addTransition(fade);        //left buttons column slide to left        Slide slide = new Slide(Gravity.LEFT);        slide.addTarget(R.id.slideLeftContainer);        set.addTransition(slide);        //right buttons column slide to right        Slide slide2 = new Slide(Gravity.RIGHT);        slide2.addTarget(R.id.slideRightContainer);        set.addTransition(slide2);        return set;    }    private Transition getScene1Transition() {        TransitionSet set = new TransitionSet();        //buttons from scene2 fade out        Fade fade = new Fade();        fade.addTarget(R.id.btnBegin);        fade.addTarget(R.id.btnCancel);        fade.addTarget(R.id.text);        set.addTransition(fade);        //Circular Reveal collapse animation starts        CircularRevealTransition crt = new CircularRevealTransition();        crt.addTarget(R.id.yellowBG);        crt.setDuration(600);        set.addTransition(crt);        //then fab button changes position        ChangeBounds changeTransform = new ChangeBounds();        changeTransform.addTarget(R.id.fab);        changeTransform.setDuration(300);        changeTransform.setStartDelay(500);        //arc path        ArcMotion arcMotion = new ArcMotion();        arcMotion.setMaximumAngle(45);        arcMotion.setMinimumHorizontalAngle(90);        arcMotion.setMinimumVerticalAngle(0);        changeTransform.setPathMotion(arcMotion);        set.addTransition(changeTransform);        //left buttons column slide in from left        Slide slide = new Slide(Gravity.LEFT);        slide.addTarget(R.id.slideLeftContainer);        slide.setStartDelay(500);        set.addTransition(slide);        //right buttons column slide in from right        Slide slide2 = new Slide(Gravity.RIGHT);        slide2.addTarget(R.id.slideRightContainer);        slide2.setStartDelay(500);        set.addTransition(slide2);        return set;    }}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity">    <android.support.v7.widget.Toolbar    android:id="@+id/toolbar"    android:layout_width="match_parent"    android:layout_height="?attr/actionBarSize"    android:background="?attr/colorPrimary"    app:layout_constraintLeft_toLeftOf="parent"    app:layout_constraintRight_toRightOf="parent"    app:layout_constraintTop_toTopOf="parent"    app:titleTextColor="#fff" />    <FrameLayout    android:id="@+id/topContainer"    android:layout_width="match_parent"    android:layout_height="@dimen/header_height"    android:background="#00BCD4"    android:orientation="vertical"    app:layout_constraintLeft_toLeftOf="parent"    app:layout_constraintRight_toRightOf="parent"    app:layout_constraintTop_toBottomOf="@+id/toolbar">        <TextView        android:layout_width="match_parent"        android:layout_height="36dp"        android:layout_gravity="bottom"        android:background="#9000"        android:gravity="center_vertical"        android:paddingLeft="8dp"        android:text="MONDAY"        android:textColor="#fff" />    </FrameLayout>    <FrameLayout    android:id="@+id/sceneRoot"    android:layout_width="match_parent"    android:layout_height="0dp"    android:clipChildren="false"    android:clipToPadding="false"    app:layout_constraintBottom_toBottomOf="parent"    app:layout_constraintLeft_toLeftOf="parent"    app:layout_constraintRight_toRightOf="parent"    app:layout_constraintTop_toBottomOf="@+id/toolbar" /></android.support.constraint.ConstraintLayout>

scene1.xml:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/root"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:clipToPadding="false"><LinearLayout    android:id="@+id/slideLeftContainer"    android:layout_width="wrap_content"    android:layout_height="match_parent"    android:layout_marginTop="@dimen/header_height"    android:orientation="vertical">    <include layout="@layout/button" />    <include layout="@layout/button" />    <include layout="@layout/button" /></LinearLayout><LinearLayout    android:id="@+id/slideRightContainer"    android:layout_width="wrap_content"    android:layout_height="match_parent"    android:layout_gravity="right"    android:layout_marginTop="@dimen/header_height"    android:orientation="vertical">    <include layout="@layout/button" />    <include layout="@layout/button" />    <include layout="@layout/button" /></LinearLayout><android.support.design.widget.FloatingActionButton    android:id="@+id/fab"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="right"    android:layout_marginTop="@dimen/fab_margin_top"    android:layout_marginRight="@dimen/fab_margin"    android:clickable="true"    android:focusable="true"    app:backgroundTint="#FFEE4D"    app:srcCompat="@drawable/ic_add_black_24dp" /></FrameLayout>

scene2.xml:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/root"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:clipToPadding="false"android:paddingTop="@dimen/header_height"><View    android:id="@+id/yellowBG"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#FFEE4D" /><android.support.design.widget.FloatingActionButton    android:id="@+id/fab"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center"    android:layout_margin="@dimen/fab_margin"    app:backgroundTint="#FFEE4D"    app:srcCompat="@drawable/ic_add_black_24dp" /><Button    android:id="@+id/btnBegin"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="bottom|left"    android:layout_margin="@dimen/fab_margin"    android:padding="16dp"    android:text="BEGIN"    android:textSize="22sp" /><TextView    android:id="@+id/text"    android:layout_width="@dimen/header_height"    android:layout_height="wrap_content"    android:layout_gravity="center"    android:gravity="center"    android:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt"    android:textSize="22sp" /><Button    android:id="@+id/btnCancel"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="bottom|right"    android:layout_margin="@dimen/fab_margin"    android:backgroundTint="#FF5151"    android:padding="16dp"    android:text="CANCEL"    android:textSize="22sp" /></FrameLayout>

button.xml:

<?xml version="1.0" encoding="utf-8"?><Button xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_margin="16dp"    android:padding="24dp"    android:text="6:30"    android:textSize="24sp" />

values.xml:

<?xml version="1.0" encoding="utf-8"?><resources>    <string name="app_name">TestTransition</string>    <color name="colorPrimary">#3F51B5</color>    <color name="colorPrimaryDark">#303F9F</color>    <color name="colorAccent">#FF4081</color>    <dimen name="fab_margin">16dp</dimen>    <dimen name="header_height">200dp</dimen>    <dimen name="fab_margin_top">132dp</dimen></resources>


I think what you're looking for are Meaningful Transitions.

In pre-lollipop devices this can be achieved with ActivityOptionsCompat helper.

Some useful links that might help you: