Android动画详解

本文内容概要

image

Tween Animation(补间动画)

补间动画,在视图容器内执行一系列变换:缩放,位移,透明度,旋转
  • TranslateAnimation 位置移动动画
  • ScaleAnimation 大小缩放动画
  • RotateAnimation 旋转动画
  • AlphaAnimation 透明度动画
  • AnimationSet 可以组合多个动画的集合一起执行
使用xml创建动画

定义单个动画:test_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator"
    android:fromXScale="1.0"
    android:toXScale="3.0"
    android:fromYScale="1.0"
    android:toYScale="3.0"
    <!-- 缩放的轴点,50%相对自身的位置 -->
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="700" />

// AnimationUtils.loadAnimation() 加载动画
Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.test_scale);
// View.startAnimation() 启动动画
mView.startAnimation(scaleAnim)

定义动画集合AnimationSet

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:shareInterpolator="@android:anim/linear_interpolator">
    <!-- pivotX pivotY 可以为百分比,也可以为具体像素值 -->
    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="90" />
    <!-- fromXDelta,fromYDelta,toXDelta,toYDelta 都是相对View于初始位置的x,y的距离;delta就是初中数学中的德尔塔Δ,一般指相对值-->
    <translate
        android:duration="1000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="200"
        android:toYDelta="200" />
    <!-- alpha值:0为全透明 1为不透明 -->
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />
    <!-- scale值为相对自身缩放比例,1为默认初始View的大小,1.2就是1.2倍 -->
    <scale
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="1.2"
        android:toYScale="1.2" />
</set>
使用Java代码创建动画
AnimationSet animationSet = new AnimationSet(false);
// 新建位移动画
TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 100);
// 新建旋转动画
RotateAnimation rotateAnimation = new RotateAnimation(0, 90,  Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// 添加动画到集合
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(translateAnimation);
// 设置集合动画时长
animationSet.setDuration(500);
animationSet.setFillAfter(false);
// 开启动画
mView.startAnimation(animationSet);

注意事项

  • xml中set标签或者AnimationSet对象的属性对他所包含的所有动画都有效,比如在set标签设置了duration,内部的动画再设置duration是无效的
  • set标签(AnimationSet)repeatMode和repeatCount无效
  • rotate 和 translate先后顺序需注意,通常把rotate放前面
  • 如果AnimationSet中同时包含scale和translate,那么scale会影响translate的x,y的值

Frame Animation(帧动画)

帧动画,顾名思义就是一帧一帧播放视图形成动画效果;对应动画类有两个:AnimationDrawable 和 AnimatedVectorDrawable

AnimationDrawable

AnimationDrawable继承自DrawableContainer,而DrawableContainer继承自Drawable,在定义xml的时候应放在drawable目录下面

这里说个题外话,其实android的资源文件放那个目录(res/下合法的任意目录)都可以,实际资源对象初始化是根据xml文件的内容而定,只是无规矩不成方圆,按约束放置资源文件便于管理维护

在xml定义AnimationDrawable
<!-- 定义:animation_drawable.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <!-- oneshot属性为true表示只执行一次,false表示无限循环; duration表示当前帧显示的时间 -->
    <item android:drawable="@drawable/frame1" android:duration="100"/>
    <item android:drawable="@drawable/frame2" android:duration="100"/>
    <item android:drawable="@drawable/frame3" android:duration="100"/>
</animation-list>

<!-- 在View中引用 animation_drawable.xml -->
<View
    id="@+id/animation_view"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="@drawable/animation_drawable" />

// 获取View的Background,开启或停止动画
View mView = findViewById(R.id.animation_view);
AnimationDrawable drawable = (AnimationDrawable) mView.getBackground();
drawable.start();
drawable.stop();
java实现AnimationDrawable
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.addFrame(getResources().getDrawable(R.drawable.frame1), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.frame2), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.frame3), 100);
view.setBackgroundDrawable(animationDrawable);
// 开始动画
animationDrawable.start();
// 停止动画
animationDrawable.stop();

注意事项

AnimationDrawable.start()方法不要在onCreate里面调用,因为这时AnimationDrawable可能还没有attach到Window上,如果需要在Activity打开就立刻播放动画,可以在:onStart() 或者 onWindowFocusChanged方法里面调用该方法

AnimatedVectorDrawable

待完善...

Property Animation API >= 11 (属性动画)

属性动画提供了一系列强大的功能,它可以用在任何对象上!简单的说就是:在动画时间里内通过回调方法不断更新View的属性从而实现动画效果!属性动画包含:ValueAnimator,ObjectAnimator,TimeAnimator,AnimatorSet

可通过Animator可修改View的常见属性如下

name desc
translationX translationY 距离View初始位置的x轴,y轴的距离,默认为0
rotationX rotationY rotationX 2D旋转,rotationY 3D旋转
scaleX scaleY x,y轴方向上的缩放
x y x = mLeft + translationX,y = mTop + translationY
alpha 透明度

属性动画原理

下面两个图是将View在40ms内向右移动40像素,每隔单位时间将view的x属性更新一下从而实现动画效果

  • 匀速移动(x的值跟时间t成直线函数关系:x = a * t, a为常量)
image
  • 加速移动(注意观察x值的变化)
image
  • 具体实现流程:都是通过设置AnimatorUpdateListener监听,在回调方法中更新View的属性


    image

ValueAnimator

用Java创建Animator
// 从0到100 再从100到2 再从2到300 再从300到500
ValueAnimator animator = ValueAnimator.ofFloat(0, 100, 2, 300, 500);
// 设置插值器,如过不设置,默认插值器为:AccelerateDecelerateInterpolator
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = animation.getAnimatedValue()
        // use value update view property
        view.setTranslationX(value)
    }
});
animator.setDuration(5000);
animator.start();
ValueAnimator提供了以下方法
  • (API21) ValueAnimator.ofArgb(int... values) : 传入几个颜色值,可以实现颜色渐变切换
  • ValueAnimator.ofFloat(float... values) : float值根据时间变化而变化
  • ValueAnimator.ofInt(int... values) : int值根据时间变化而变化

ObjectAnimator

ObjectAnimator继承自ValueAnimator,把上面ValueAnimation.AnimatorUpdateListener.onAnimationUpdate()方法里面需要干的事情封装好了,只需要将需要动画的View和对应属性传递给ObjectAnimator,它会自动完成属性更新,从而实现动画!

比如下面代码修改:textView的translationX值

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
这里textView为需要修改属性的对象,translationX为被修改的属性,这里需要textView对象拥有名称为set+TranslationX的方法,方法名的set后面第一个字符必须为大写,方法输入参数也应该为float,上面例子对应的set方法为:setTranslationX(float translationX);至于方法的访问域private还是public都无所谓,反正都是通过反射调用!

AnimatorSet

AnimatorSet跟AnimationSet不同,AnimationSet用于将多个动画合并一起执行,而AnimatorSet用于管理多个Animator执行的顺序。它可以并行执行,也可以设定依赖关系按一定顺序执行

* 并行执行:play(anim1).with(anim2) 或者 playTogether(Animator... items)
* 先后执行:playSequentially(Animator... items)
* 依赖执行:play(anim1).before(anim2).after(anim3).with(anim4)
通过play,widh,before,after,playSequentially,playTogether的组合可以创建各种执行顺序,形成不同的动画效果
示例如下
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

PropertyValuesHolder 和 ViewPropertyAnimator

一个ObjectAnimator只能修改一个属性,如果需要同时修改多个属性需要创建多个ObjectAnimator,再通过AnimatorSet.playTogether(Animator... items)执行!通过PropertyValuesHolder可以只创建一个ObjectAnimator来实现

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 300f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 200f);
ObjectAnimator.ofPropertyValuesHolder(textView, pvhX, pvhY)
        .setDuration(3000)
        .start();

使用ViewPropertyAnimator就更简单了

// 这里我们没有手动调用start()方法,他会在合适时机自动执行
textView.animate().x(300f).y(400).setDuration(3000);

Keyframe

关键帧,有时你需要在特定时间点指定特定的状态,比如用1000ms将View向右移动300px,但是在100ms时移动到150px,200ms时移动到200px,最终1000ms时移动到300px

//创建关键帧
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(0.1f, 150f);
Keyframe kf2 = Keyframe.ofFloat(0.2f, 200f);
Keyframe kf3 = Keyframe.ofFloat(1.0f, 300f);
//创建动画
PropertyValuesHolder pvhTranslationX = PropertyValuesHolder.ofKeyframe("translationX", kf0, kf1, kf2, kf3);
ObjectAnimator transAnim = ObjectAnimator.ofPropertyValuesHolder(textView, pvhTranslationX);
transAnim.setInterpolator(new LinearInterpolator());
transAnim.setDuration(5000);
transAnim.start();
Keyframe.ofFloat(float, float)方法,第一个参数是整个动画时间的分子数,取值为0-1;0表示起始时间点,1表示结束时间点,第二个参数表示在第一个参数时间点上Animator对应的Value值
PropertyValuesHolder保存了动画要操作的属性和关键帧数据,用于创建ObjectAnimator

StateListAnimator

StateListAnimator可以在View状态改变时启动动画,比如在Button按下时,让Button放大一点,透明度变为80%;当按钮恢复时,大小和透明度恢复原状。

res/xml/animate_scale.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 120%, alpha to 0.7 -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="alpha"
                android:valueTo="0.8f"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="scaleX"
                android:valueTo="1.2"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="scaleY"
                android:valueTo="1.2"
                android:valueType="floatType" />
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100%, alpha to 1.0 -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="alpha"
                android:valueTo="1.0f"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="scaleX"
                android:valueTo="1"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="scaleY"
                android:valueTo="1"
                android:valueType="floatType" />

        </set>
    </item>
</selector>
通过stateListAnimator属性引用animate_scale.xml
<Button
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:stateListAnimator="@xml/animate_scale"
    android:text="Login" />
或者使用AnimatorInflater.loadStateListAnimator()和View.setStateListAnimator()方法使用StateListAnimator

TimeAnimator(API Level 16)

提供了一个简单的回调机制,通过 TimeAnimator.TimeListener,在动画的每一帧处通知你。它没有持续时间,插值器或是对象值设定。回调监听器为每一帧动画接受信息,包括总运行时间和从前一帧到现在的运行时间

相关方法

  • setTimeListener(TimeAnimator.TimeListener listener) 设置监听器

使用xml定义Animator

在Android 3.1(API 12)及以上可以通过xml定义animator,存放在res/animator目录下
如下:property_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <!-- ordering执行顺序,together或sequentially,默认为together-->
    <!-- 这里需注意valueType不要搞错了,可选值有:floatType,intType,colorType-->
    <set>
        <objectAnimator
            android:duration="500"
            android:propertyName="x"
            android:valueTo="400"
            android:valueType="floatType" />
        <objectAnimator
            android:duration="500"
            android:propertyName="y"
            android:valueTo="300"
            android:valueType="floatType" />
    </set>
    <objectAnimator
        android:duration="500"
        android:propertyName="alpha"
        android:valueTo="0.3f"
        android:valueType="floatType" />
</set>
通过AnimatorInflater加载Animator
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(mContext, R.animator.property_animator);
set.setTarget(textView);
set.start();
ValueAnimator也可以用xml定义
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />
在Java中使用
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float) updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});
xmlAnimator.start();

Dynamic Animation(动态动画)

DynamicAnimation是由support包提供的动画工具,使用需添加如下依赖
implementation 'com.android.support:support-dynamic-animation:28.0.0'
// or
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'

DynamicAnimation有两个实现类:

  • FlingAnimation 用于速度手势动画
  • SpringAnimation 弹性动画

由于比较简单,直接上代码

FlingAnimation

LinearLayout linearLayout = findViewById(R.id.layout);
// linearLayout为目标动画对象,DynamicAnimation.SCROLL_X为scrollX属性
FlingAnimation fling = new FlingAnimation(linearLayout, DynamicAnimation.SCROLL_X);
// 可操作的最小单位
fling.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS);
// 滑动起始速度
fling.setStartVelocity(4000)
        // 最小距离
        .setMinValue(0)
        // 最大距离
        .setMaxValue(1000)
        // 滑动摩擦系数,相当于加速度
        .setFriction(1.1f)
        .start();

SpringAnimation

View ball = findViewById(R.id.ball);
// 构造函数最后一个参数是最终的位置
SpringAnimation spring = new SpringAnimation(ball, DynamicAnimation.X, 0);
// 设置起始速度
spring.setStartVelocity(3000);
// 设置弹性的强度,参考SpringForce对象内部定义的常量
spring.getSpring().setStiffness(SpringForce.STIFFNESS_HIGH);
// 设置弹性比例,参考SpringForce对象内部定义的常量
spring.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY);
// 设置起始位置,如果不设置就为View初始位置
spring.setStartValue(500);
spring.start();

Scene Animation (API Level 19)

场景动画又可以称作为过渡动画,就是在同一个Layout容器中切换不同布局文件时的过渡动画,比如淡入淡出效果,如果两个布局文件里面包含相同id的View,系统会把他们的位置,大小,旋转,透明度等属性关联起来,形成过渡的动画效果!
先看个示例
image

在使用场景动画之前,我们先看几个类:

Scene

一个scene包含了场景切换的根容器ViewGroup,以及一个对应场景的ViewGroup

Transition

Transition包含了动画的相关信息,他主要负责2件事情:一是获取始末场景的属性的值(比如:位置,大小,透明度等);二是根据始末场景属性值计算并播放动画。主要的实现类有:

  • ChangeBounds 负责View的位置属性
  • ChangeTransform 负责缩放和旋转属性
  • ChangeClipBounds 负责mClipBounds属性
  • ChangeScroll 负责scroll属性
  • Fade 负责透明度
  • Slide 负责添加或者移除时,滑入添加,滑出删除效果
  • ChangeImageTransform 负责ImageView的matrix属性
  • TransitionSet 多个Transition集合,可以一起执行:ORDERING_TOGETHER,也可以串行执行:ORDERING_SEQUENTIAL

TransitionManager

TransitionManager则包含了一系列Transition,并且在场景切换时执行这些Transition。以下两个方法实现了场景切换:

  • TransitionManager.go(Scene scene)
  • TransitionManager.go(Scene scene, Transition transition)

TransitionManager.go(Scene scene)方法,内部自定义了AutoTransition对象,它是TransitionSet的子类,内部添加了Fade和ChangeBounds两个过渡动画,并且是串行执行的!

TransitionManager.go(Scene scene, Transition transition)方法则可以传入自定义的Transition,自定义需要的动画效果,以及串行还是并行执行!

Layout,Scene,Transition,TransitionManager之间的关系图

image

使用方法

综上几个类的说明,基本使用方法也比较明确了

  • 第一步:定义Scene
  • 第二步:定义Transition
  • 第三部:调用TransitionManager的go方法切换场景

定义Scene

mSceneRoot = (ViewGroup) view.findViewById(R.id.scene_root);

mScene1 = new Scene(mSceneRoot, (ViewGroup) mSceneRoot.findViewById(R.id.container));

// 通过layout xml文件创建scene
mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, getActivity());

mScene3 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene3, getActivity());

定义Transition

public class MyTransition extends TransitionSet {

    public MyTransition() {
        init();
    }

    public MyTransition(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        // 串行执行
        setOrdering(ORDERING_SEQUENTIAL);

        // 添加淡出效果
        Fade fadeOut = new Fade(Fade.OUT);
        fadeOut.setDuration(1000);
        addTransition(fadeOut);
        // 位置迁移动画
        ChangeBounds changeBounds = new ChangeBounds();
        changeBounds.setDuration(1000);
        // 缩放,旋转动画
        ChangeTransform transform = new ChangeTransform();
        transform.setDuration(1000);
        // 将位置迁移和缩放旋转放到一个set里面,并行执行
        TransitionSet set = new TransitionSet();
        set.setOrdering(TransitionSet.ORDERING_TOGETHER);
        set.addTransition(changeBounds);
        set.addTransition(transform);
        addTransition(set);

        // 添加淡入效果
        Fade fadeIn = new Fade(Fade.IN);
        fadeIn.setDuration(1000);
        addTransition(fadeIn);
    }
}

切换场景

// 使用默认AutoTransition
TransitionManager.go(mScene2)
TransitionManager.go(mScene3)

// 使用自定义Transition
TransitionManager.go(mScene2, new MyTransition())
TransitionManager.go(mScene3, new MyTransition())

xml也可以定义场景动画,放在res/transition目录下面

scene3_transition_manager.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
    <transition
        android:toScene="@layout/scene3"
        android:transition="@transition/changebounds_fadein_together"/>
</transitionManager>
changebounds_fadein_together.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 位置信息动画 -->
    <changeBounds
        android:duration="1000"/>
    <fade android:fadingMode="fade_in"
        android:duration="300">
        <targets>
            <!-- 这里可以自定义添加需要动画的View,对应Java方法Transition.addTarget(View target)-->
            <target android:targetId="@id/transition_title" />
        </targets>
    </fade>
</transitionSet>
在java中调用
mTransitionManagerForScene3 = TransitionInflater.from(getActivity())
                .inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot);
mTransitionManagerForScene3.transitionTo(mScene3)

Layout Animation (API Level 11)

在ViewGroup添加或者移除子View时的动画效果

使用很简单在xml中将android:animateLayoutChanges属性设置为true即可

<LinearLayout android:id="@+id/container"
    android:animateLayoutChanges="true"
    ...
/>

也可以通过代码设置Transition

viewGroup.setLayoutTransition(new LayoutTransition())

LayoutTransition提供了一些set方法可以修改一些动画信息,比如:

  • setDuration(int transitionType, long duration)
  • setInterpolator(int transitionType, TimeInterpolator interpolator)
  • setAnimator(int transitionType, Animator animator)
  • .......

上面这些方法有个共同的参数transitionType,有以下5个值:

  • APPEARIN:元素在容器中显现时需要动画显示。
  • CHANGE_APPEARING:由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
  • DISAPPEARING:元素在容器中消失时需要动画显示。
  • CHANGE_DISAPPEARING:由于容器中某个元素要消失,其它元素的变化需要动画显示。
  • CHANGING:当元素本身某个属性发生变化,但元素并没有添加和移除时需要动画的显示

添加删除动画效果

image

Activity Animation (API Level 21)

Activity切换动画跟上面的场景动画其实差不多,只不过场景动画是在一个视图容器中切换View,Activity动画是在页面切换时播放动画。

Activity支持页面切换整体动画效果:

  • explode 爆破进入
  • slide 滑入滑出
  • fade 淡入淡出

同时也支持两个Activity中相关联的View的过渡动画,关联View支持以下属性:

  • changeBounds 位置属性
  • changeClipBounds ClipBounds属性
  • changeTransform 缩放和旋转属性
  • changeImageTransform ImageView的matrix属性

在xml中定义Activity过渡动画

<style name="BaseAppTheme" parent="android:Theme.Material">
  <!-- enable window content transitions -->
  <item name="android:windowActivityTransitions">true</item>

  <!-- specify enter and exit transitions -->
  <item name="android:windowEnterTransition">@transition/explode</item>
  <item name="android:windowExitTransition">@transition/explode</item>

  <!-- specify shared element transitions -->
  <item name="android:windowSharedElementEnterTransition">
    @transition/change_image_transform</item>
  <item name="android:windowSharedElementExitTransition">
    @transition/change_image_transform</item>
</style>
change_image_transform.xml
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
  <changeImageTransform/>
</transitionSet>

也可以通过Java代码设置

  • getWindow().setEnterTransition(Transition transition);
  • getWindow().setEnterTransition(Transition transition);

在两个Activity中创建View关联动画

FirstActivity.java 中将
// 通过makeSceneTransitionAnimation方法创建动画,将需要关联动画的View和一个TransitionName关联起来,
// 同时在第二个Activity中通过ViewCompat.setTransitionName(View view, String transitionName)也将动画名和View关联起来
ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
    new Pair<View, String>(view.findViewById(R.id.imageview_item), DetailActivity.VIEW_NAME_HEADER_IMAGE),
    new Pair<View, String>(view.findViewById(R.id.textview_name), DetailActivity.VIEW_NAME_HEADER_TITLE));
ActivityCompat.startActivity(this, intent, activityOptions.toBundle());
DetailActivity.java
public class DetailActivity extends Activity {
    public static final String VIEW_NAME_HEADER_IMAGE = "detail:header:image";
    public static final String VIEW_NAME_HEADER_TITLE = "detail:header:title";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.details);

        mHeaderImageView = (ImageView) findViewById(R.id.imageview_item);
        mHeaderTitle = (TextView) findViewById(R.id.textview_title);
        // 将view和TransitionName绑定
        ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
        ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
    }
}

效果图

效果图

参考内容: Google官方文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349