Android动画——Activity转场动画|过渡动画一点薄见(一)((Transition Animation 系列))

Transition Animation系列文章

  1. Android动画——Activity转场动画|过渡动画一点薄见(一)((Transition Animation 系列))
  2. android动画——过渡动画中ActivityOptions介绍与使用((Transition Animation 系列))

1. 转场动画概述

在Android开发过程中,经常会遇到Activity之间切换的问题,转场动画就是用于布局(界面)变化时的过渡动画,即不同UI状态转换时的动画。

Transition过渡动画(转场动画)是在android4.4.2中引入的,那时只能对整个Activity或Fragment做动画,Google在Android5.0的Material Design中引入更为完整的Transition框架。

Android的过渡动画可分为四个部分

  • Activity/Fragment切换时内容过渡动画
  • Activity/Fragment切换时共享元素过渡动画
  • 同一个页面中的场景过渡动画
  • 共享元素过渡动画 + 揭露效果

在具体学习使用过渡动画前,先花点时间捋一下当中的两个重要概念:场景(scenes)转换(transitions);场景定义了一个确定的UI状态,而转换定义了两个场景切换的动画。

当两个场景切换时,Transition主要有以下两个行为:

  • 确定开始场景和结束场景中每个view的状态
  • 根据状态差异创建Animator,用以场景切换时View的动画

下面来根据过渡动画的四个部分分别介绍,这里以Activity为例,Fragment类似。

2. Activity/Fragment切换时内容过渡动画

关于Activity/Fragment切换,这里可以细分为四个动画,例如有两个Activity,分别是A和B:

  • A启动B:A发生了exit动画,B发生了enter动画
  • B返回A:A发生了reenter动画,B发生了return动画
    在这里插入图片描述
    在这里插入图片描述

在Android 5.0(API)中默认提供了三种转换:

  • 分解(Explode):从场景中心移入或移出视图
  • 滑动(Slide):从场景边缘移入或移移出视图
  • 淡入淡出(Fade):通过调整透明度在场景中增添或移除视图

2.1 Android 5.0之前

在Android 5.0之前,一般是用overridePendingTransition()这个方法来实现内容过渡:

 * @param enterAnim A resource ID of the animation resource to use for
     * the incoming activity.  Use 0 for no animation.
     * @param exitAnim A resource ID of the animation resource to use for
     * the outgoing activity.  Use 0 for no animation.
     */
    public void overridePendingTransition(int enterAnim, int exitAnim) {
        try {
            ActivityManager.getService().overridePendingTransition(
                    mToken, getPackageName(), enterAnim, exitAnim);
        } catch (RemoteException e) {
        }
    }

其中enterAnim是进入动画,exitAnim是退出动画

关于该方法的使用十分简单,先在XML中定义好两个Activity的切换动画,然后:

startActivity(intent);
overridePendingTransition(R.anim.bottom_top_anim, R.anim.alpha_hide);

---

finish();
overridePendingTransition(R.anim.alpha_show, R.anim.top_bottom_anim);

备注

  • overridePendingTransition方法必须在startActivity()或者finish()方法后面
  • 如果参数是0,则表示没有动画

2.2 Android 5.0 之后

在Android 5.0 之后,要启用内容过渡动画需要完成下面几个步骤:

  1. 打开内容转换开关

在style中设置

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

或者在代码中设置

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}
  1. 指定进入、退出转换
    (1)可以在style中指定转换:示例
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- specify enter and exit transitions -->
  <item name="android:windowEnterTransition">@transition/activity_fade</item>
  <item name="android:windowExitTransition">@transition/activity_slide</item>
</style>

转换文件存放在res/transiton目录下

res/transition/activity_fade.xml

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/"
    android:duration="1000"/>

res/transition/activity_slide.xml

<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/"
    android:duration="1000"/>

(2)除了在style中指定转换,也可以在代码中指定转换

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        setContentView(R.layout.activity_main);
        setupWindowAnimations();
        TextView textView = (TextView) findViewById(R.id.start_activity);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());
            }
        });

    }

    private void setupWindowAnimations() {
        // inflate from xml
        Slide slide = (Slide) TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
        // or create directly
//        Slide slide = new Slide();
//        slide.setDuration(1000);
//        slide.setSlideEdge(Gravity.LEFT);
        getWindow().setExitTransition(slide);
        getWindow().setReenterTransition(slide);
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        setContentView(R.layout.activity_second);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        // inflate from xml
        Fade fade = (Fade) TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
        // or create directly
//        Fade fade = new Fade();
//        fade.setDuration(1000);
        getWindow().setEnterTransition(fade);
    }

从上述的代码示例中,总结如下

  • 转场动画在代码中是通过获取Window对象进行设置的,关于转场动画有四种场景

    • void setEnterTransition(Transition transition) 当前界面进入动画
    • void setExitTransition (Transition transition) 当前界面退出动画
    • void setReenterTransition (Transition transition) 下个界面返回当前界面时,当前界面进入动画
    • void setReturnTransition (Transition transition) 返回上个界面时当前界面的退出动画
  • 在xml中设置转场动画也有四种场景

    • android:windowEnterTransition
    • android:windowExitTransition
    • android:windowReenterTransition
    • android:windowReturnTransition
  • 退出activity或调用了finish()时,记得调用finishAfterTransition(),保证执行退出动画。

mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finishAfterTransition();
                finish();
            }
        });

2.3 选择动画的目标

前面做转场动画时,默认情况过渡动画会对所有的子View进行遍历加载动画,如果时添加目标则不会进行遍历所有子View,或者可以排除特定的View

对于目标,一般有三种操作:

  • 添加:默认会进行遍历所有的视图加载动画, 但是如果使用了添加就不会遍历所有, 只会让指定的视图进行动画
  • 排除:如果使用排除方法, 依旧会进行遍历视图对象, 不过会排除你指定的视图
  • 删除:删除目标是在动画已经遍历视图完成以后还想对目标集合进行变更, 就可以删除指定的视图

添加/排除/删除目标支持以下参数类型

  1. 视图对象(View)
  2. 过渡名(TransitionName)
  3. 字节码(Class)
  4. ID
Transition addTarget (View target)

Transition addTarget (String targetName)

Transition addTarget (Class targetType)

Transition addTarget (int targetId)

相应的对于删除或者排除,则分别是:removeTarget()excludeTraget()

使用示例:

private void setupWindowAnimations() {
        // inflate from xml
        Fade fade = (Fade) TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
        fade.excludeTarget(android.R.id.navigationBarBackground,true);
        fade.excludeTarget(android.R.id.statusBarBackground,true);
        Slide slide = (Slide) TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
        slide.setSlideEdge(Gravity.LEFT);
        slide.setDuration(2000);
        slide.excludeTarget(android.R.id.navigationBarBackground, true);	// 	排除导航栏
        slide.excludeTarget(android.R.id.statusBarBackground,true);	//排除状态栏
        slide.removeTarget(mTextView);
        getWindow().setEnterTransition(slide);
        getWindow().setReturnTransition(slide);
//        getWindow().setReenterTransition(fade);
//        getWindow().setExitTransition(slide);
    }

2.4 内容过渡动画原理

在文章开头部分就阐述过,内容过渡动画创建之前,必须要确定视图的状态信息,这个动作需要修改所有过渡视图(transitioning view)的可见性。

  1. Activity A 调用startActivity()
    • 系统遍历 A 的视图节点,找到A的exit transition运行时哪些view会退出场景
    • A 的退出转换记录A的所有过渡视图的开始状态
    • 系统将所有过渡视图的可见性设置为INVISIBLE
    • 在下一帧,A 的退出转换记录所有过渡视图的结束状态
    • A 的退出转换比较每个过渡视图的开始状态和结束状态,然后创建 Animator 作为退出动画,运行该动画
  2. Activity B启动
    • 系统遍历 B 的视图节点,找到B的enter transition运行时的所有过渡视图,设置这些过渡视图的可见性为INVISIBLE
    • B 的进入转换记录所有过渡视图的开始状态
    • 系统将所有过渡视图的可见性设置为VISIBLE
    • 在下一帧,B 的进入转换记录所有过渡视图的结束状态
    • B 的进入转换比较每个过渡视图的开始状态和结束状态,然后创建 Animator 作为进入动画,运行该动画。

总结
所有的内容转换都需要记录每个过渡视图的开始状态和结束状态。而抽象类Visibility已经做了这部分内容了,Visibility的子类只需要实现 onAppear()onDisappear() 方法,创建过渡视图进入或退出场景的 Animator。Android 5.0 中Visibility有三个子类 – FadeSlideExplode,如果有需要的话也可以自定义Visibility子类。

关于Visibility的三个子类的使用探索,待补充。。。。。。。。。。。。。。。

2.5 Transition Overlap

一般默认情况下,内容过渡动画的进入/返回转换会在退出/重新进入转换结束前一点点开始,产生一个小的重叠来让整体的效果更自然、协调,如果不想产生重叠等效果,可以通过Window/Fragment 的setAllowEnterTransitionOverlap(boolean)setAllowReturnTransitionOverlap(boolean)方法来设置。默认overlap时true,进入转换会在退出转换开始后尽可能快地开始,如果设置为false,进入转换转换只能在退出转换结束后开始。这个对Activity和Fragment的共享元素过渡动画同样有效。

 private void setupWindowAnimations() {
        // inflate from xml
        Slide slide = (Slide) TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
        // or create directly
//        Slide slide = new Slide();
//        slide.setDuration(1000);

        slide.setSlideEdge(Gravity.LEFT);
        getWindow().setAllowEnterTransitionOverlap(true);
        getWindow().setAllowReturnTransitionOverlap(true);
        getWindow().setExitTransition(slide);
        getWindow().setReenterTransition(slide);
    }

除了在代码中设置,还可以在xml中设置:

<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- specify transition overlap -->
  <item name="android:windowAllowEnterTransitionOverlap">true</item>
  <item name="android:windowAllowReturnTransitionOverlap">true</item>
</style>

2.6 自定义Visibility

如果你要实现一个动画,google官方提供的slide、fade等都难以实现你的需求的时候,你可以自己写一个转场动画。

自定义转场动画,只需要继承Visibility或者Transition,其中Visibility是继承自Transibility

如果转场动画只是某个View出现或消失,那么可以考虑继承Visibility,如果是类ShareElement(下面介绍的共享元素)这样的转场动画,那么就需要继承Transition

继承Visibility需要重写4个方法:

  • captureStartValues():保存计算动画初始状态的一个属性值
  • captureEndValues():保存计算动画结束状态的一个属性值
  • onAppear():如果是进入动画,即显示某个View会执行这个方法
  • onDisappear():如果是退出,即不显示某个View会执行这个方法

示例:

public class FABTransition extends Visibility {
	private static final String BOTTOM_TRANSITION_Y = "FABTransition:change_transY:transitionY";
	private static final String TAG = "FABTransition";
    private View fab;
    private Context context;

    public FABTransition(Context context, View fab) {
        this.fab = fab;
        this.context = context;
    }

    /**
     * 收集动画的开始信息
     * @param transitionValues 只有两个成员变量view和values, view指的是我们要从哪个view上收集信息, values是用来存放我们收集到的信息的
     *                         比如: 在captureStartValues里, transitionValues.view指的就是我们在开始动画的界面上的那个view,
     *                         在captureEndValues指的就是在目标界面上的那个view
     */
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        int transY= (int) (context.getResources().getDisplayMetrics().density*56*2);
        transitionValues.values.put(BOTTOM_TRANSITION_Y,transY);

    }

    /**
     * 收集动画结束的信息
     */
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
        transitionValues.values.put(BOTTOM_TRANSITION_Y, 0);
    }

    /**
     * 创建一个Animator
     */
    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        return super.createAnimator(sceneRoot, startValues, endValues);
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }
        int startY= (int) startValues.values.get(BOTTOM_TRANSITION_Y);
        int endY= (int) endValues.values.get(BOTTOM_TRANSITION_Y);
        //	在这里去除之前存储的初始值和结束值,然后执行动画
        if(view==fab && startY!=endY){
            ValueAnimator valueAnimator=ValueAnimator.ofInt(startY,endY);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    Object transY= animation.getAnimatedValue();
                    if(transY!=null){
                        view.setTranslationY((Integer) transY);
                    }

                }
            });
            return valueAnimator;
        }
        return null;
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }
        int startY= (int) endValues.values.get(BOTTOM_TRANSITION_Y);
        int endY= (int) startValues.values.get(BOTTOM_TRANSITION_Y);
        if(view==fab && startY!=endY){
            ValueAnimator valueAnimator=ValueAnimator.ofInt(startY,endY);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    Object transY= animation.getAnimatedValue();
                    if(transY!=null){
                        view.setTranslationY((Integer) transY);
                    }

                }
            });
            return valueAnimator;
        }

        return null;
    }
}

使用自定义的FABTransition ,为它添加一个Target:

TransitionSet cotentTransition=new TransitionSet();
        Slide slide=new Slide(Gravity.LEFT);
        slide.setDuration(500);
        slide.excludeTarget(android.R.id.navigationBarBackground, true);
        slide.excludeTarget(android.R.id.statusBarBackground, true);
        slide.excludeTarget(R.id.appBarLayout, true);
        slide.excludeTarget(R.id.fab, true);
        cotentTransition.addTransition(slide);
        //fab进入动画
        FABTransition fabTransition=new FABTransition(thisthis);
        fabTransition.addTarget(R.id.fab);
        fabTransition.setDuration(500);
        cotentTransition.addTransition(fabTransition);
        getWindow().setEnterTransition(cotentTransition);

3. Activity/Fragment切换时的共享元素过渡动画

不同activity或fragment之间传统的过渡涉及到整个View层级相互独立的进入和退出过渡动画。例如,渐入过渡,滑动过渡,或新引入的爆炸过渡。

然后,大多数情况下页面之间有共用的元素,并且在页面切换时有能力提供这些共享元素分别强调连续性的过渡作为用户操作的App。

在这里插入图片描述
shareElement Transition指的是共享元素从activity/fragment到其他activity/fragment时的动画,其过渡动画在确定开始状态和结束状态是分别在两个页面上,可以实现共享元素从一个页面到另一个页面的动画。

Android 5.0(API 21)默认提供了下面四种共享元素转换:

  • changeBounds - 为目标视图的布局布局边界的变化添加动画

  • changeClipBounds - 为目标视图的裁剪边界的变化添加动画

  • changeTransform - 为目标视图的缩放与旋转变化添加动画

  • changeImageTransform - 为目标图像的大小与缩放变化添加动画

与内容过渡动画类似,共享元素过渡动画也分为 sharedElementExitTransitionsharedElementEnterTransitionsharedElementReturnTransitionsharedElementReenterTransition

在这里插入图片描述

3.1 共享动画使用

一般启用了共享元素过渡动画,都会启用内容过渡动画。

注意:共享元素过渡需要Android 5.0(API 21)及以上并且将会忽略低版本。使用API 21指定特性之前确保在运行时检查版本。

  1. 打开窗口内容过渡开关
    styles.xml文件中开启窗口内容过渡
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- enable window content transitions -->
  <item name="android:windowContentTransitions">true</item>
</style>

或者在代码中设置:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}

这一步与内容过渡动画的第一步是一样的。

  1. 为共享元素指定统一的 transition name

使用共享元素的布局中指定共用过渡名称。使用android:transitionName属性。

activity_main.xml

<TextView
        android:id="@+id/start_shared_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:transitionName="profile"
        android:layout_gravity="center_horizontal"
        android:text="start shared activity"
        android:textSize="25sp"/>

activity_shared_element.xml

<TextView
        android:id="@+id/shared_element_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:transitionName="profile"
        android:text="这里是共享元素页面"/>

处理在xml文件文件中通过android:transitionName属性设置外,还可以在代码中通过ViewCompat.setTranslationName()方法设置

ViewCompat.setTransitionName(view,"imageView");
  1. 启动目标Activity,带上共享元素参数

使用ActivityOpstions.makeSceneTransitionAnimation()方法来指定共享元素的 origin viewtransition name。如果要结束第二个 Activity 时也显示共享元素过渡动画,请调用Activity.finishAfterTransitino()而非Activity.finish()

textView1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SharedElementActivity.class);
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this,textView1, "profile").toBundle());
            }
        });

如果你想要从源view层级动画多个元素,可以通过在源View和目标view使用不同的过渡名称实现。

textView1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SharedElementActivity.class);
                Pair<View, String> p1 = Pair.create((View)textView1, "profile");
                Pair<View, String> p2 = Pair.create((View)textView, "test");

                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this,p1,p2).toBundle());
            }
        });

注意不要使用过多的共享元素。它会使场景有一个粘性直到动画从一个屏幕到另一个屏幕(可能会包含多个共享元素),过多的共享元素会导致用户分心产生糟糕的体验

  1. 在目标Activity中设置共享元素动画
 		ChangeImageTransform changeImageTransform = new ChangeImageTransform();
        ChangeBounds changeBounds = new ChangeBounds();
        ChangeClipBounds changeClipBounds = new ChangeClipBounds();
        TransitionSet transitionSet=new TransitionSet();


        transitionSet.addTransition(changeImageTransform);
//        transitionSet.addTransition(changeBounds);
        transitionSet.addTransition(changeClipBounds);
        transitionSet.addTarget(R.id.image);
        transitionSet.setDuration(1000);
        getWindow().setSharedElementEnterTransition(transitionSet);
//        getWindow().setSharedElementReturnTransition(transitionSet);

3.2 自定义Transition

Transition动画其实就是拿着第一页某个view的信息去第二页的某个view上做的动画, 这样我们在视觉上就会产生一个渐变的错觉。前面我们大致学习了该类动画的使用方法。对于共享动画,google提供了ChangeBounds, ChangeTransform, ChangeImageTransform, 和 ChangeClipBounds四种动画,基本能够适用于大多数典型场景。你也可以自定义自己的Transition。这里我们开始自己自定义Transition,用于针对不同的view采用不同的动画效果。

自定义Transition,与自定义View等一样,我们需要继承Transition类,主要重写三个方法

  • 开始值captureStartValues(TransitionValues transitionValues)
  • 结束值captureEndValues(TransitionValues transitionValues)
  • 创建动画createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues)

关于这三个方法中的一个参数TransitionValues:这个类很简单,只有两个成员变量viewvalues。view指的是我们要从哪个view上收集信息, values是用来存放我们收集到的信息的

示例:

public class ColorTransition extends Transition {

    private static final String COLOR_BACKGROUND = "ColorTransition:change_color:background";
    private int mStartColor;
    private int mEndColor;

    public ColorTransition(int mStartColor, int mEndColor) {
        this.mStartColor = mStartColor;
        this.mEndColor = mEndColor;
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        transitionValues.values.put(COLOR_BACKGROUND,mStartColor);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        transitionValues.values.put(COLOR_BACKGROUND,mEndColor);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }

        final View view = endValues.view;

        int startColor = (int) startValues.values.get(COLOR_BACKGROUND);
        int endColor = (int) endValues.values.get(COLOR_BACKGROUND);

        if (startColor != endColor) {
            ValueAnimator animator = ValueAnimator.ofArgb(startColor, endColor);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    Object value = animation.getAnimatedValue();
                    if (null != value) {
                        view.setBackgroundColor((Integer) value);
                    }
                }
            });
            return animator;
        }
        return null;
    }
}

3.3 共享元素过渡动画原理

共享元素过的动画的原理与内容过渡动画类似,系统也会在创建共享元素动画前直接修改共享视图的属性。当Activity A启动ActivityB时:

  • A调用startActivity()后,B 启动时,窗口的背景是透明的。
  • 统以 A 为标准重新设置 B 的每个共享元素视图的大小和位置,过一会 B 的进入转换会记录 B 中所有共享元素的开始状态,而对于内容过渡来说,其他的 transitioning view 的可见性都是 INVISIBLE
  • 系统再重新将 B 的每个共享元素视图的大小和位置设置为原来的样子,过一会 B 的进入转换会记录 B 中所有共享元素的结束状态。
  • B 的进入转换比较每个共享元素的开始状态和结束状态,创建 Animator 作为共享元素动画。
  • 系统将隐藏 A 的所有共享元素视图,然后开始运行 B 的共享元素动画。在 B 的共享元素动画过程中,B 的窗口背景会逐渐变为不透明的。

对比内容过渡动画,内容过渡动画中系统会修改 transition views 的可见性,而共享元素过渡动画中系统会修改 shared element views 的位置、大小和显示。而且我们也可以看出实际上共享元素的 view 其实并没有在 Activity/ Fragment 之间共享,事实上,我们看到的进入或者返回的共享元素过渡动画都是直接在 B 的视图中运行的。

3.4 Shared Element Overlay

在这里插入图片描述
Window中有个关于共享元素的设置setSharedElementsUseOverlay(boolean sharedElementsUseOverlay),我们将其设为false,重启App:

在这里插入图片描述

默认情况下,共享元素视图是绘制在整个视图结构之上的——窗口的ViewOverlay。添加到视图的ViewOverlay层的Drawables或者Views都会在所有其他视图上面绘制,不会被遮挡。而共享元素默认会在 ViewOverlay 层绘制的原因是:共享元素是整个进入转换中的焦点,如果 transitioning view 忽然遮盖了共享元素的话,整体的效果会大打折扣。

虽然共享元素视图默认是绘制在 ViewOverlay 层的,但是也可以在必要情况下使用 Window.setSharedElementsUseOverlay(false) 来禁用。

3.5 延迟加载

Transition会获取共享视图的前后状态值来创建动画,如果我们的view(例如图片)是网上下载的,那么很有可能图片的准确大小需要下载下来才能确定,Activity Transition API提供了一对方法暂时推迟过渡,直到我们确切地知道共享元素已经被适当的渲染和放置。在在onCreate中调用postponeEnterTransition()(API >= 21)或者supportPostponeEnterTransition()(API < 21)延迟过渡;当图片的状态确定后,调用startPostponedEnterTransition()(API >= 21)或supportStartPostponedEnterTransition()(API < 21)恢复过渡,常见处理:

// ... load remote image with Glide/Picasso here

supportPostponeEnterTransition();
ivBackdrop.getViewTreeObserver().addOnPreDrawListener(
    new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            ivBackdrop.getViewTreeObserver().removeOnPreDrawListener(this);
            supportStartPostponedEnterTransition();
            return true;
        }
    }
);

3.6 SharedElementCallback

这部分内容待更新。。。。。

猜你喜欢

转载自blog.csdn.net/weixin_43499030/article/details/90413758