Material Design Animation

切换Activity的Transition动画:

需要开启如下属性:

<item name="android:windowContentTransitions">true</item>

如果需要设置动画切换时是否允许覆盖,即在前一个完全动画消失,后一个可以开始动画出现,即两个Activity的动画顺序关系,如果为false,则是前一个动画完全消失后,后一个才开始动画出现;为true时,则两个动画几乎同时开始:

<item name="android:windowAllowEnterTransitionOverlap">false</item>
<item name="android:windowAllowReturnTransitionOverlap">false</item>

本Activity设置的Transition动画,只应用在本Activity,与切换中的另一个Activity无关。Transition除了选择样式外,还需为该Activity的不同场景设置Transition。

Transition在窗口中的应用场景

ReenterTransition(Activity从stop中resume时)、ExitTransition(Activity切换中被stop时使用)、EnterTranstion(窗口启动时)、ReturnTransition(窗口关闭时)。

Transition有哪些样式:https://developer.android.com/reference/android/transition/Transition.html

根据以上关系可以看出,可以实现Transition或它的子类可以实现定义Transition动画样式。

Transition的XML属性

XML attributes

android:duration Amount of time (in milliseconds) that the transition should run. 
android:interpolator Interpolator to be used in the animations spawned by this transition. 
android:startDelay Delay in milliseconds before the transition starts. 

Fade动画非继承的XML属性:

XML attributes

android:slideEdge

动画可以在XML文件中定义,也可以在代码中创建。放在XML文件需要放在res/transition/下。

Transition的xml格式如下:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode/>
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <changeImageTransform/>
</transitionSet>

也可单独一个元素:

<changeBounds/>

使用自定义Transition时的格式:

<transition class="my.app.transition.CustomTransition"/>

explode.xml:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode
        android:duration="@integer/anim_duration_long"
        android:interpolator="@android:interpolator/bounce" />
</transitionSet>

 slide.xml:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <slide
        android:duration="@integer/anim_duration_long"
        android:slideEdge="bottom" />
</transitionSet>

1.设置slide动画:

    private void setupWindowAnimations() {
        // Re-enter transition is executed when returning to this activity
        Slide slideTransition = new Slide();
        slideTransition.setSlideEdge(Gravity.LEFT);
        slideTransition.setDuration(getResources().getInteger(R.integer.anim_duration_long));
        getWindow().setReenterTransition(slideTransition);
        getWindow().setExitTransition(slideTransition);
    }

使用Scene:

1.从当前显示的Layout中选择一个ViewGroup作为SceneRoot。

2.准备好一个Layout,使用Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, context);获取一个Scene对象scene0

3.TransitionManager.go(scene0)或者TransitionManager.go(scene0,transition);

代码片段示例:

    private void setupLayout() {
        final ViewGroup activityRoot = (ViewGroup) findViewById(R.id.buttons_group);
        ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

        scene0 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this);
        scene0.setEnterAction(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < viewsToAnimate.size(); i++) {
                    View child = viewsToAnimate.get(i);
                    child.animate()
                            .setStartDelay(i * DELAY)
                            .scaleX(1)
                            .scaleY(1);

                }
            }
        });
        scene0.setExitAction(new Runnable() {
            @Override
            public void run() {
                TransitionManager.beginDelayedTransition(activityRoot);
                View title = scene0.getSceneRoot().findViewById(R.id.scene0_title);
                title.setScaleX(0);
                title.setScaleY(0);
            }
        });


        scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this);
        scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene2, this);
        scene3 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene3, this);
        scene4 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene4, this);

        View button1 = findViewById(R.id.sample3_button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TransitionManager.go(scene1, new ChangeBounds());
//                TransitionManager.go(scene1);
            }
        });
        View button2 = findViewById(R.id.sample3_button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TransitionManager.go(scene2, TransitionInflater.from(AnimationsActivity2.this).
                        inflateTransition(R.transition.slide_and_changebounds));
            }
        });

        View button3 = findViewById(R.id.sample3_button3);
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TransitionManager.go(scene3, TransitionInflater.from(AnimationsActivity2.this).
                        inflateTransition(R.transition.slide_and_changebounds_sequential));
            }
        });

        View button4 = findViewById(R.id.sample3_button4);
        button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TransitionManager.go(scene4, TransitionInflater.from(AnimationsActivity2.this).
                        inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            }
        });

        viewsToAnimate.add(button1);
        viewsToAnimate.add(button2);
        viewsToAnimate.add(button3);
        viewsToAnimate.add(button4);
    }

其中sceneRoot是一个FrameLayout,当Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this)时,sceneRoot中所有Child都会被移除,R.layout.activity_animations_scene0会add到sceneRoot中,作为sceneRoot的childView。当Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this)时,会把R.layout.activity_animations_scene0这个View从sceneRoot中移走,然后activity_animations_scene1添加到sceneRoot中。

Transition可以是TransitionSet,而其中各个动画可以是并行执行的,也可以是串行执行的,默认是并行的,如果要串行的话,如下定义xml:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/anim_duration_long"
    android:transitionOrdering="sequential">
    <slide />
    <changeBounds />
</transitionSet>

Transition中的Slide、Fade和属性动画中translateX/Y、alpha类似,但是更加复杂,所以效果表现更佳。而相对来说explode、changeBounds、ChangeImageTransform就更加复杂了。explode动画效果是root中的childView向某个中心点合过来或者远离中心点。changeBounds则是会旋转移动,但是前后两个Scene的Layout中的view的id要有相同的才有效果,如下两个Layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">


    <ImageView
        android:id="@+id/square_green"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle_24dp"
        android:layout_alignParentRight="true"
        android:tint="@color/sample_green" />

    <ImageView
        android:id="@+id/square_blue"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_blue" />

    <ImageView
        android:id="@+id/square_red"
        style="@style/MaterialAnimations.Icon.Big"
        android:layout_below="@+id/square_green"
        android:layout_alignParentRight="true"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_red" />

    <ImageView
        android:id="@+id/square_yellow"
        style="@style/MaterialAnimations.Icon.Big"
        android:layout_below="@+id/square_blue"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_yellow" />
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">


    <ImageView
        android:id="@+id/square_green"
        style="@style/MaterialAnimations.Icon.Big"
        android:layout_below="@id/square_blue"
        android:layout_alignParentRight="true"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_green" />

    <ImageView
        android:id="@+id/square_blue"
        style="@style/MaterialAnimations.Icon.Big"
        android:layout_alignParentRight="true"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_blue" />

    <ImageView
        android:id="@+id/square_red"
        style="@style/MaterialAnimations.Icon.Big"
        android:layout_below="@+id/square_yellow"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_red" />

    <ImageView
        android:id="@+id/square_yellow"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle_24dp"
        android:tint="@color/sample_yellow" />
</RelativeLayout>

 

public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition)

 调用上面的方法后,sceneRoot代表的控件树发生任何显示上的变化,都会应用transition动画去展示,即本帧与下一帧之间使用过渡动画;比如执行以下代码:

    private void changeLayout() {
        TransitionManager.beginDelayedTransition(viewRoot);

        ViewGroup.LayoutParams params = square.getLayoutParams();
        if (sizeChanged) {
            params.width = savedWidth;
        } else {
            savedWidth = params.width;
            params.width = 200;
        }
        sizeChanged = !sizeChanged;
        square.setLayoutParams(params);
    }

会导致重新布局并重绘,那么就会产生transition动画。又比如以下PopupWindow的代码:

        private void startEnterTransition(Transition enterTransition) {
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                enterTransition.addTarget(child);
                child.setVisibility(View.INVISIBLE);
            }

            TransitionManager.beginDelayedTransition(this, enterTransition);

            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                child.setVisibility(View.VISIBLE);
            }
        }

会产生一个动画,因为View从不可见到可见了。那前后两帧不同了,这个两帧使用什么动画去过渡,就看传入的transition参数了。

使用Transition.addTarget(View)选择指定的childView变化才使用过渡动画:

                    Transition transiton = new Explode();
//linearLayout包括了两个button,linearLayout visibility变化时指定只对bt1做explode动画
                    linearLayout.setVisibility(View.INVISIBLE);
                    transiton.addTarget(bt1);
                    transiton.setDuration(1000);
                    TransitionManager.beginDelayedTransition(l,transiton);
                    linearLayout.setVisibility(View.VISIBLE);

在通过WindowManager.addView添加窗口时,可以使用以下代码模拟进入窗口的动画:

        public void requestEnterTransition(Transition transition) {
            final ViewTreeObserver observer = getViewTreeObserver();
            if (observer != null && transition != null) {
                final Transition enterTransition = transition.clone();

                // Postpone the enter transition after the first layout pass.
                observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        final ViewTreeObserver observer = getViewTreeObserver();
                        if (observer != null) {
                            observer.removeOnGlobalLayoutListener(this);
                        }

                        final Rect epicenter = getTransitionEpicenter();
                        enterTransition.setEpicenterCallback(new EpicenterCallback() {
                            @Override
                            public Rect onGetEpicenter(Transition transition) {
                                return epicenter;
                            }
                        });
                        startEnterTransition(enterTransition);
                    }
                });
            }
        }        

private void startEnterTransition(Transition enterTransition) {
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                enterTransition.addTarget(child);
                child.setVisibility(View.INVISIBLE);
            }

            TransitionManager.beginDelayedTransition(this, enterTransition);

            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                child.setVisibility(View.VISIBLE);
            }
        }

Shared Element Transition

1.在两边Activity的layout中都通过android:transitionName或者通过代码View.setTransitionName设置sharedElement的共同name。如果时Activity切换,那么待消失那边的layout可以不设置transitionName,因为ActivityOption中已经指定了哪个对象作为共享元素。

2.创建一个ActivityOption:

ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
        Pair.create(view1, "agreedName1"),
        Pair.create(view2, "agreedName2"));

3.使用上面的ActivityOption启动Activity:

Activity.startActivity(intent, options.toBundle())

使用切换Frament时也可以使用SharedElement:

1.在两边Fragment的layout中都通过android:transitionName或者通过代码View.setTransitionName设置sharedElement的共同name

2.

        getFragmentManager().beginTransaction()
                .replace(R.id.viewgroup, fragment)
                .addToBackStack(null)
                .addSharedElement(view, getString(R.string.view_id_name))
                .commit();

上面的view是待消失的fragment中的view对象,view_id_name是待显示的fragment的view的transitionName。

设定SharedElement使用哪种动画,如果没有设置,则是默认的Transition:

Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
        getWindow().setSharedElementEnterTransition(transition);
        transition.addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
            }

            @Override
            public void onTransitionEnd(Transition transition) {
                // Removing listener here is very important because shared element transition is executed again backwards on exit. If we don't remove the listener this code will be triggered again.
                transition.removeListener(this);
                hideTarget();
                animateRevealShow(toolbar);
                animateButtonsIn();
            }

            @Override
            public void onTransitionCancel(Transition transition) {
            }

            @Override
            public void onTransitionPause(Transition transition) {
            }

            @Override
            public void onTransitionResume(Transition transition) {
            }
        });

只要设置了Transition,无论是从xml设置的还是从代码中设置的,都可以通过getWindow().getxxxTransition()方法设置监听器或者其他东西

 

Reveal Transition

//viewRoot是需要应用暴露动画的View,x,y为暴露中心,暴露开始半径,暴露终点半径
Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, x, y, 0, finalRadius);
        viewRoot.setBackgroundColor(ContextCompat.getColor(this, color));
        anim.setDuration(getResources().getInteger(R.integer.anim_duration_long));
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.start();

让各个View有序动画入场和进场

    private void animateButtonsIn() {
        for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
            View child = bgViewGroup.getChildAt(i);
            child.animate()
                    .setStartDelay(100 + i * DELAY)
                    .setInterpolator(interpolator)
                    .alpha(1)
                    .scaleX(1)
                    .scaleY(1);
        }
    }

    private void animateButtonsOut() {
        for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
            View child = bgViewGroup.getChildAt(i);
            child.animate()
                    .setStartDelay(100 + i * DELAY)
                    .setInterpolator(interpolator)
                    .alpha(0)
                    .scaleX(0f)
                    .scaleY(0f);
        }
    }

监听动画各个生命周期

监听Scene:

scene0 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this);
        scene0.setEnterAction(new Runnable() {
            @Override
            public void run() {
             //在进入该Scene前

                }
            }
        });
        scene0.setExitAction(new Runnable() {
            @Override
            public void run() {
             在退出该Scene前
            }
        });

监听Transition:

        transition.addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
            }

            @Override
            public void onTransitionEnd(Transition transition) {

            }

            @Override
            public void onTransitionCancel(Transition transition) {
            }

            @Override
            public void onTransitionPause(Transition transition) {

            }

            @Override
            public void onTransitionResume(Transition transition) {

            }
        });

参考自:https://github.com/lgvalle/Material-Animations/(这个是一个很的全面Transition Demo)

              https://github.com/googlesamples/android-ActivitySceneTransitionBasic 

猜你喜欢

转载自blog.csdn.net/b1480521874/article/details/88244066