Android动画之Transition和TransitionManager使用

动画入门和进阶文章列表:

Animation动画概述和执行原理
Android动画之补间动画TweenAnimation
Android动画之逐帧动画FrameAnimation
Android动画之插值器简介和系统默认插值器
Android动画之插值器Interpolator自定义
Android动画之视图动画的缺点和属性动画的引入
Android动画之ValueAnimator用法和自定义估值器
Android动画之ObjectAnimator实现补间动画和ObjectAnimator自定义属性
Android动画之ObjectAnimator中ofXX函数全解析-自定义Property,TypeConverter,TypeEvaluator
Android动画之AnimatorSet联合动画用法
Android动画之LayoutTransition布局动画
Android动画之共享元素动画
Android动画之ViewPropertyAnimator(专用于view的属性动画)
Android动画之Activity切换动画overridePendingTransition实现和Theme Xml方式实现
Android动画之ActivityOptionsCompat概述
Android动画之场景变换Transition动画的使用
Android动画之Transition和TransitionManager使用
Android动画之圆形揭露动画Circular Reveal

1 Transition概述

Transition 在前一篇已经简单讲解了它的使用Android动画之场景变换Transition动画的使用,这次接着讲解Transition的其他用法和TransitionManager。

Transition内部使用了属性动画实现,所以它可以认为是属性动画的封装。Transition两个核心概念为:场景(scenes)和变换(transitions),场景是UI当前状态,变换则定义了在不同场景之间动画变化的过程。所以Transition主要负责两个方面的事,一是保存开始和结束场景的两种状态,二是在两种状态之间创建动画。由于场景记录了内部所有View的开始和结束状态,所以Transition动画更具连贯性。谁执行动画呢?TransitionManager负责执行动画的任务。

2 Scene 场景

Scene 场景 场景过渡动画就是实现View从一种状态变化到另外一种状态,Scene就代表一个场景,它内部保存一个完整地视图结构,从根ViewGroup到所有子view,还有它们的所有状态信息。所以Scene最终就一个设置了不同属性特征的ViewGroup。

Scene构造函数
Scene(ViewGroup sceneRoot)
sceneRoot:根ViewGroup,用于承载Scene view
Scene(ViewGroup sceneRoot, View layout)
参数说明:
sceneRoot:根ViewGroup
layout:view布局

Scene一般利用getSceneForLayout()函数生成Scene场景
Scene.getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
参数说明
sceneRoot:根ViewGroup,内部包含多个场景viewGroup。
layoutId:view的布局文件资源id,代表一个场景。
context:上下文
代码示例:

Activity layout

<?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=".Main9Activity">

<FrameLayout
    android:id="@+id/viewcontainer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
</FrameLayout>

</android.support.constraint.ConstraintLayout>

scene1 layout

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
android:id="@+id/imageview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/image_home_game_nor"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="10dp"/>

    <ImageView
android:id="@+id/imageview2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/image_home_game_nor2"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="10dp"
        app:layout_constraintRight_toRightOf="parent"/>

</android.support.constraint.ConstraintLayout>

scene2 layout 只是图片调换了位置

生成Scene方法一:

Scene scene1 = Scene.getSceneForLayout(mContainer, R.layout.scene1, Main9Activity.this);
Scene scene2 = Scene.getSceneForLayout(mContainer, R.layout.scene2, Main9Activity.this);

除了利用getSceneForLayout外,根据Scene的构造函数,我们还可以传入View直接构造Scene。
生成Scene方法二:

View view1 = LayoutInflater.from(Main9Activity.this).inflate(R.layout.scene1, null);
View view2 = LayoutInflater.from(Main9Activity.this).inflate(R.layout.scene2, null);

Scene scene1 = new Scene(mContainer, view1);
Scene scene2 = new Scene(mContainer, view1);

3 TransitionManager

TransitionManager在场景变换时控制transitions的执行。通过TransitionManager可以添加场景和Transition变换,但为场景变化设置一个默认transitions是没有必要的,因为默认会使用AutoTransition。

TransitionManager当场景变换时开启动画的方式:
beginDelayedTransition(ViewGroup sceneRoot, Transition transition)
beginDelayedTransition(ViewGroup sceneRoot)
场景变幻时传入场景的view根sceneRoot,和transition动画。如果不指定Transition,默认为AutoTransition。

go(Scene scene, Transition transition)
go(Scene scene)
go的方式需要传入scene,scene由Scene利用view生成。如果不指定Transition,则默认为AutoTransition。

Transiton系统效果

继承Transiton类的效果

  • ChangeBounds:检测view的位置边界创建移动和缩放动画
  • ChangeTransform:检测view的scale和rotation创建缩放和旋转动画
  • ChangeClipBounds:检测view的剪切区域的位置边界,和ChangeBounds类似。不过ChangeBounds针对的是view而ChangeClipBounds针对的是view的剪切区域(setClipBound(Rect rect) 中的rect)。如果没有设置则没有动画效果
  • ChangeImageTransform:检测ImageView的尺寸,位置以及ScaleType,并创建相应动画。
  • Fade,Slide,Explode:根据view的visibility的状态执行渐入渐出,滑动,分解动画。

go 两个场景切换

public class Main9Activity extends AppCompatActivity {
    private FrameLayout mContainer;
    boolean togger = true;
    Scene scene1 ;
    Scene scene2 ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main9);
        mContainer = findViewById(R.id.viewcontainer);
        mContainer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (togger){
                    TransitionManager.go(scene1,new ChangeBounds());
                }else{
                    TransitionManager.go(scene2,new ChangeBounds());
                }
                togger = !togger;
            }
        });
        init();
    }

    private void init() {
         scene1 = Scene.getSceneForLayout(mContainer, R.layout.scene1, Main9Activity.this);
         scene2 = Scene.getSceneForLayout(mContainer, R.layout.scene2, Main9Activity.this);
   }

Android动画之Transition和TransitionManager使用_第1张图片

如果不指定Transition为ChangeBounds。

mContainer.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (togger){
           // TransitionManager.go(scene1,new ChangeBounds());
            TransitionManager.go(scene1);
        }else{
            TransitionManager.go(scene2);
        }
        togger = !togger;
    }
});

Android动画之Transition和TransitionManager使用_第2张图片

效果图虽然没有指定Transition但是也有动画效果,其实如果不指定Transition,系统默认的Transition为AutoTransition。

AutoTransition关键源码:

private void init() {
    setOrdering(ORDERING_SEQUENTIAL);
    addTransition(new Fade(Fade.OUT)).
            addTransition(new ChangeBounds()).
            addTransition(new Fade(Fade.IN));
}

AutoTransition是个组合变换。

slide效果
TransitionManager.go(scene1,new Slide());
Android动画之Transition和TransitionManager使用_第3张图片

Explode效果
TransitionManager.go(scene1,new Explode());
Android动画之Transition和TransitionManager使用_第4张图片

上面的例子都是对于xml layout 文件做变换,下面我们对已存在sceneRoot中的view做动画

Activity的layout:

<?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=".Main9Activity">

<FrameLayout
    android:id="@+id/viewcontainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent">
    <FrameLayout
    android:id="@+id/viewcontainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

 <ImageView
            android:layout_gravity="center"
            android:id="@+id/imageview1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_home_game_nor"
            android:scaleType="matrix"
            android:layout_marginTop="10dp"/>

        <ImageView
            android:layout_gravity="center"
            android:id="@+id/imageview2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_home_game_nor2"
            android:scaleType="matrix"
            android:layout_marginTop="10dp"/>
</FrameLayout>
      
</FrameLayout>

</android.support.constraint.ConstraintLayout>

然后取imageview1生成Scene,

imageView = mContainer.findViewById(R.id.imageview1);

imageView = mContainer.findViewById(R.id.imageview1);
        imageView2 = mContainer.findViewById(R.id.imageview2);
        scene1 = new Scene(mContainer, imageView);
        scene2 = new Scene(mContainer, imageView2);

Container.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (togger){
             Transition transition = new Fade();
                    transition.setDuration(1000);
                    TransitionManager.go(scene1,transition);
        }else{
            Transition transition = new Fade();
                    transition.setDuration(1000);
                    TransitionManager.go(scene2,transition);
        }
        togger = !togger;
    }
});

运行出错:
Process: com.ldx.canvasdrawdemo, PID: 18825
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

注意:
利用Scene构造函数生成Scene时,需要sceneRoot,sceneRoot在动画开始时,会将sceneRoot中的所有子View都remove掉,然后在sceneRoot中加载结束场景。通过代码new Scene(mSceneRoot, view)生成Scene,view必须是sceneRoot的直接子view,或者view是没有parentview的,不然在addview的时候会报错。

修改如下:
让图片作为sceneRoot的直接子view。

<?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=".Main9Activity">

<FrameLayout
    android:id="@+id/viewcontainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent">
        <ImageView
            android:layout_gravity="center"
            android:id="@+id/imageview1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_home_game_nor"
            android:scaleType="matrix"
            android:layout_marginTop="10dp"/>

        <ImageView
            android:layout_gravity="center"
            android:id="@+id/imageview2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_home_game_nor2"
            android:scaleType="matrix"
            android:layout_marginTop="10dp"/>

</FrameLayout>

</android.support.constraint.ConstraintLayout>
  mContainer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
             
                if (togger){
                  Transition transition = new Fade();
                    transition.setDuration(1000);
                    TransitionManager.go(scene1,transition);

                }else{
                   Transition transition = new Fade();
                    transition.setDuration(1000);
                    TransitionManager.go(scene2,transition);
                }
                togger = !togger;
            }
        });

Android动画之Transition和TransitionManager使用_第5张图片

只对布局中的某些view做动画

scene1和scene2 xml文件类似,只列举scene1 layout 文件

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/image_home_game_nor"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="10dp"/>

    <ImageView
        android:id="@+id/imageview2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/image_home_game_nor2"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
       />

</android.support.constraint.ConstraintLayout>

可以利用Transition的addTarget(),removeTarget(),只对某些view做动画,或者不对某些view做动画。
如果调用了addTarget则只对调用了这个函数的View做动画,其他View直接完成最终状态,如果调用了removeTarget则是对没有调用这个函数的其他view做动画。如果同时调用了两个函数,则调用removeTarget会从调用了addTarget中的view查找,然后剔除。

mContainer.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (togger){
            Transition transition = new ChangeBounds();
            transition.addTarget(R.id.imageview1);
            TransitionManager.go(scene1,transition);
        }else{
            Transition transition = new ChangeBounds();
            transition.addTarget(R.id.imageview1);
            TransitionManager.go(scene2,transition);
           }
        togger = !togger;
    }
});

只对其中一个ImageView做动画:
Android动画之Transition和TransitionManager使用_第6张图片

可以看到效果图中,有一个ImageView没有动画效果。

beginDelayedTransition(ViewGroup sceneRoot, Transition transition)

TransitionManager.go函数需要生成对应的Scene,beginDelayedTransiton则不需要,只需要填入sceneRoot和Transition就可以实现Transition动画。

这个函数里面没有Scene,那它何时执行动画呢,很简单当view的某些属性信息改变时,就会执行动画,上面介绍go函数时使用了ChangeTransform,ChangeBounds,ChangeClipBounds
,ChangeImageTransform,但感觉他们效果差不多,其实这些属性主要用于beginDelayedTransition这个函数,当对应的属性改变时,会自动触发Transition动画。

执行TransitionManager.beginDelayedTransition后,系统会保存一个当前视图树状态的场景,修改view的属性信息,在下一次绘制时,系统会自动对比之前保存的视图树,然后执行一步动画
重要提醒:如果想让beginDelayedTransition有效果,必须每次改变视图属性之后,重新调用beginDelayedTransition,或者改变之前调用beginDelayedTransition,这样才能够保存当前view的状态,否则存储的属性没有改变,不会有动画效果。

ChangeBounds尺寸改变

mContainer.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Transition transition = new ChangeBounds();
        transition.setDuration(1000);
        TransitionManager.beginDelayedTransition(mContainer,transition);
        if (togger){
                      FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) imageView.getLayoutParams();
            layoutParams1.height =100;
            layoutParams1.width =100;
            imageView.setLayoutParams(layoutParams1);
        }else{
            FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) imageView.getLayoutParams();
            layoutParams2.height = 700;
            layoutParams2.width = 700;
            imageView.setLayoutParams(layoutParams2);
       }
        togger = !togger;
    }
});

Android动画之Transition和TransitionManager使用_第7张图片
Transition 缩放效果。
ChangeImageTransform

<ImageView
    android:layout_gravity="center"
    android:id="@+id/imageview1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:src="@drawable/image_home_game_nor"
    android:scaleType="matrix"
    android:layout_marginTop="10dp"/>

ImageView的scaleType 设置为matrix

mContainer.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Transition transition = new ChangeImageTransform();
        transition.setDuration(1000);
        TransitionManager.beginDelayedTransition(mContainer,transition);
        if (togger){
            imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
        }else{
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        }
        togger = !togger;
    }
});

效果图:
Android动画之Transition和TransitionManager使用_第8张图片
利用matrix缩放旋转ImageView,然后设置Transition,没有发现动画效果,有点疑惑还在看。

你可能感兴趣的:(android动画系列,Android动画学习)