How to animate floating action button using android activity transition?

后端 未结 2 1309
南笙
南笙 2020-12-04 06:26

I am planning to implement something like this in one of my project but the concept for the following is unclear to me, there\'s no such tutorial available either, moreover

相关标签:
2条回答
  • 2020-12-04 07:03

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

    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.

    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 {
    
    @Override
    public 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;
    }
    
    @Override
    public 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>
    
    0 讨论(0)
  • 2020-12-04 07:03

    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:

    • a simple material design tutorial (with source code) which uses ActivityOptionsCompat for transitions.
    • Transitions example on the Android Developrs blog (scroll down a bit).
    • Defining Custom Animations in the Android Developers training
      guide
    0 讨论(0)
提交回复
热议问题