Android 使用ActivityOptions实现Activity转场动画

之前一直都是用这种方式实现Activity的转场动画:

// MainActivity
overridePendingTransition(enterAnim, exitAnim);

从Android5.0之后,Google提供了一种新的方式来实现:ActivityOptions。

前提
在使用前,需要声明允许使用ActivityOptions。
在styles.xml文件,设置App主题时,添加android:windowContentTransitions的属性,属性值为true:


<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <item name="android:windowContentTransitions">trueitem>
    style>
resources>

注意:这个是5.0才支持的属性,应该放到values-v21文件夹下。不过,即使我不提醒你,编译器也不会放过你的,哈哈!!

使用及效果
ActivityOptionsCompat是ActivityOptions的兼容包,这个类可以帮助我们实现Activity转场动画。

ActivityOptionsCompat的方法不多,有如下几个:

makeCustomAnimation(Context context, int enterResId, int exitResId)

makeScaleUpAnimation(View source, int startX, int startY, int startWidth, int startHeight)

makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)

makeClipRevealAnimation(View source, int startX, int startY, int width, int height)

makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)

makeSceneTransitionAnimation(Activity activity, Pair… sharedElements)

接下来我们一一介绍这几个方法的使用方法和效果吧。

makeCustomAnimation
转场效果:

用户自己定义,看下去就明白了。
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.anim_activity_in, R.anim.anim_activity_out);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeCustomAnimation方法需要3个参数(结合例子):

this:Context类型,也就是Activity。
R.anim.anim_activity_in:int类型,新Activity显示动画。
R.anim.anim_activity_out:int类型,当前Activity退出动画。
这后2个参数有木有想到什么,没错,相当于Activity的:

overridePendingTransition(enterAnim, exitAnim);

如果你要使用这种方式,还不如用Activity原生的overridePendingTransition方法转场。

makeScaleUpAnimation
转场效果:

新Activity会以某个点为中心,从某个大小开始逐渐放大到最大。
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeScaleUpAnimation(view, view.getWidth() / 2, view.getHeight() / 2, 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeScaleUpAnimation需要5个参数(结合例子):

view:View类型,指定从哪个View的坐标开始放大。
view.getWidth() / 2:int类型,指定以View的X坐标为放大中心的X坐标。
view.getHeight() / 2:int类型,指定以View的Y坐标为放大中心的Y坐标。
0:int类型,指定放大前新Activity是多宽。
0:int类型,指定放大前新Activity是多高。
makeThumbnailScaleUpAnimation
转场效果:

放大一个图片,最后过度到一个新activity(我测试的时候,效果不明显)
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeThumbnailScaleUpAnimation(view, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeThumbnailScaleUpAnimation需要4个参数(结合例子):

view:View类型,要放大的图片从哪个View的左上角的坐标作为中心点放大。
BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher):Bitmap类型,指定要放大的图片。
0:放大前图片的初始宽度。
0:放大前图片的初始高度。
makeClipRevealAnimation
转场效果:

与makeScaleUpAnimation效果差不多,可能是时间太短的问题,看不清楚
1
使用方式:

ActivityOptionsCompat compat4 = ActivityOptionsCompat.makeClipRevealAnimation(view, view.getWidth() / 2, view.getHeight() / 2, 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat4.toBundle());

参数同makeScaleUpAnimation相同,不做详解。

makeSceneTransitionAnimation(重点)
转场效果:

两个Activity的两个View协同完成转场,也就是原Activity的View过度到新Activity的View,原新两个Activity的View的transitionName相同。
1
使用实例:

// activity_main.xml
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="com.johan.demo.MainActivity">

    ...

    "@+id/ImgView1"
        android:layout_width="160dp"
        android:layout_height="90dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" 
        />



// activity_second.xml
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...

    "@+id/ImgView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" 
        />



// strings.xml

    "tu">tu


// MainActivity使用ActivityOptions
...
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(this, imgView, getString(R.string.tu));
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());
...

注意,activity_main.xml和activity_second.xml文件都有一个ImageView,他们的android:transitionName的值是一样的(@string/tu),当使用makeSceneTransitionAnimation进行转场时,ImgView1会慢慢的过渡到ImgView2。过渡的效果,有很多种情况,例子中的过渡效果是ImgView1会逐渐放大,并移动到ImgView2的位置,同时显示SecondActivity(新Activity)。

makeSceneTransitionAnimation方法需要3个参数(结合例子):

this:Context类型,指定Activity。
imgView:View类型,指定从哪里开始过渡。例子中是ImgView1过渡到ImgView2,所以是ImgView1。
getString(R.string.tu):String类型,指定android:transitionName的值,过渡View的标志。
我想说,这种效果很酷的,一定要自己试一下!!!

makeSceneTransitionAnimation(重点)
转场效果:

makeSceneTransitionAnimation是单个View协作,makeSceneTransitionAnimation允许多个View协作,效果和makeSceneTransitionAnimation像相似。
1
使用实例:

// activity_main.xml
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="com.johan.demo.MainActivity">

    ...

    "@+id/ImgView1"
        android:layout_width="160dp"
        android:layout_height="90dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" 
        />

    "@+id/TxtView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#00ff00"
        android:textSize="16sp"
        android:text="SceneTransitionAnimation2 Text String"
        android:transitionName="@string/zi" 
        />



// activity_second.xml
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...

    "@+id/ImgView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" 
        />

    "@+id/TxtView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#0000ff"
        android:textSize="16sp"
        android:text="SceneTransitionAnimation2 Text String"
        android:transitionName="@string/zi" 
        />



// strings.xml

    "tu">tu
    "zi">zi


// MainActivity使用ActivityOptions
...
Pair imgViewPair = Pair.create(imgView, getString(R.string.tu));
Pair txtViewPair = Pair.create(txtView, getString(R.string.zi));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(this, imgViewPair, txtViewPair);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());
...

注意,transitionName值一定要一一对应哦!!

定义转场动画
除了上面动画,Android还支持我们定义转场动画。

目前支持3种Activity进退场// explode(分解)
从场景中心移入或移出视图

// slide(滑动)
从场景边缘移入或移出视图

// fade(淡出)
通过调整透明度在场景中增添或移除视图支持4种共享元素(也就是transitionName相同的View [ 不同的Activity ])过渡动画:

// changeBounds
改变目标视图的布局边界

// changeClipBounds
裁剪目标视图边界

// changeTransform
改变目标视图的缩放比例和旋转角度

// changeImageTransform
改变目标图片的大小和缩放比例

使用实例
使用方式有2种:

  • 通过xml设置
  • 通过代码设置

通过xml设置
(1)定义转场和过渡动画的xml

在res文件夹下新建transition文件夹,然后在res/transition文件夹下新建xml文件:

// transition_explode.xml

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
transitionSet>

// transition_fade.xml

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <fade android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
transitionSet>

上面我定义了2个转场动画,当然你还可以定义共享元素的过渡动画,这里只是简单定义了一下。

(2)设置SecondActivity的转场动画

因为将要启动的SecondActivity,那我们就定义SecondActivity主题时,设置SecondActivity进出场动画:


<style name="SecondActivityTheme" parent="AppTheme">
    -- 进场(Activity进入时)动画 -->
    <item name="android:windowEnterTransition">@transition/transition_explode
    
    "android:windowExitTransition">@transition/transition_fade
style>

然后设置SecondActivity的主题为SecondActivityTheme。

(3)转场
在MainActivity跳转到SecondActivity:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    // 我们这里没有用ActivityCompat转场
    startActivity(new Intent(this, SecondActivity.class), ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

因为只有5.0才支持,所以稍微做一下判断。

通过代码设置
通过代码设置,就不一定要定义xml,因为代码中也有方法支持,待会再说。

(1)定义转场和过渡动画的xml(不一定需要,你可以定义)

在res文件夹下新建transition文件夹,然后在res/transition文件夹下新建xml文件:


<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
    <fade android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
transitionSet>

(2)在代码中设置

如果我们定义了转场动画xml文件,可以在SecondActivity的onCreate方法这么设置:

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.transition_explode);
            getWindow().setEnterTransition(transition);
        }
    }
}

可以利用TransitionInflater获取到我们定义的Transition动画。

如果没有定义,可以这么设置:

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().setEnterTransition(new Explode());
        }
    }
}

new Explode()为Android提供的方法,还有其他的,自己慢慢体会。

遇到的坑
这里我有个疑问,无论是xml方式设置,还是通过代码设置,我只设置了进场动画,为什么出场动画也一样?而且如果我设置了出场动画,好像也没有效果??

原来是我设置的不对!!!

我天真的以为设置android:windowExitTransition属性是SecondActivity退出来的动画!!!

我查看到有博客解释了进出场相关资料,才明白:

// setEnterTransition() 
// android:windowEnterTransition  A start B时,使B中的View进入场景的transitionB中设置

// setExitTransition()  
// android:windowExitTransition  A start B时,使A中的View退出场景的transitionA中设置

// setReturnTransition() 
// android:windowReturnTransition B 返回 A时,使B中的View退出场景的transitionB中设置

// setReenterTransition() 
// android:windowReenterTransitionB 返回 A时,使A中的View进入场景的transitionA中设置

知道这个,我们改正之前错误的写法:

<

style name="SecondActivityTheme" parent="AppTheme">
    
    <item name="android:windowEnterTransition">@transition/transition_explodeitem>
    
    <item name="android:windowReturnTransition">@transition/transition_fadeitem>
style>

代码设置也一样哦!!

更多的转场动画组合得靠自己慢慢摸索,多点实践就行!!

参考资料
Android关于setExitTransition() 没有效果的问题
Android 5.0学习之Activity过渡动画

你可能感兴趣的:(Android 使用ActivityOptions实现Activity转场动画)