★20.转场动画

普通转场动画

1. 准备工作

方式一

Activity.onCreate()setContentView()前调用以下代码。

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

方式二

AndroidManifest.xml 里:


2. 创建Transition

方式一:通过XML创建

  1. 创建 res/transition/details_window_return_transition.xml 文件:
    
    
        
            
                
                
            
        
        
            
                
            
        
        
            
                
            
        
    
    
  2. 在代码中载入 XML 文件定义的Transition
    TransitionInflater transitionInflater = TransitionInflater.from(this);
    Transition transition = transitionInflater.inflateTransition(R.transition.details_window_return_transition);
    

方式二:通过代码创建

Explode explode = new Explode();
explode.addTarget(android.R.id.statusBarBackground);
explode.excludeTarget(android.R.id.navigationBarBackground, true);
Fade fade = new Fade();
Slide slide = new Slide();
TransitionSet transitionSet = new TransitionSet()
        .setDuration(500)
        .addTransition(explode)
        .addTransition(slide)
        .addTransition(fade);

3. 设置Transition

代码设置

设置方法

  • ActivityonCreate()onCreateView()中使用getWindow()设置动画。
  • FragmentonCreate()onCreateView()中使用getActivity().getWindow()设置动画。

动画种类

  • setExitTransition():当 A 启动 B 时,使 A 中的View退出场景的transition
  • setEnterTransition():当 A 启动 B 时,使 B 中的View进入场景的transition
  • setReturnTransition():当 B 返回 A 时,使 B 中的View退出场景的transition
  • setReenterTransition():当 B 返回 A 时,使 A 中的View进入场景的transition

XML设置

AndroidManifest.xml 里:


res/values/theme.xml 里:


    

4. 针对ViewGroup处理

  • 默认情况下,无法将ViewGroup当做一个view来处理,需要在ViewGroup的对应 XML 文件中开启TransitionGroup属性。设置ViewGroup背景色属性也有同样的效果,即便背景是透明的。
  • 若一个Transition中包含了没有开启TransitionGroup属性的ViewGrouptargetId,则此Transition不会运行。

5. 启动Activity

startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), (Pair[]) null).toBundle());

监听Transition动画

工具类:TransitionListenerAdapter

getWindow().getEnterTransition().addListener(new TransitionListenerAdapter() { });

常见问题

  • 如果没有任何效果,检查是否把 动画种类 弄错了。

共享元素转场动画

简单方式

1. 为共享元素设置TransitionName

为两个场景想要共享的View调用setTransitionName()为相同可标识的字符串。

view.setTransitionName(/* 可标识字符串 */);

2. 启动Activity

ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), view, view.getTransitionName());
startActivity(intent, optionsCompat.toBundle());

常见问题

  • 状态栏导航栏 显示不正常:可以把 导航栏状态栏 作为共享元素。
View statusBar = getActivity().findViewById(android.R.id.statusBarBackground);
View navigationBar = getActivity().findViewById(android.R.id.navigationBarBackground);
List> pairs = new ArrayList<>();
if (statusBar != null) {
    pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
}
if (navigationBar != null) {
    pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
}
// noinspection unchecked
startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), pairs.toArray(new Pair[pairs.size()])).toBundle());

复杂方式

  • 以下场景需要使用复杂方式来实现共享元素动画:
    • 当共享的View延迟加载时,如网络请求、FragmentViewPager等场景,需要复杂方式来处理。
    • 共享的ViewA启动BB返回A 发生了改变,导致不是同一个View时。

0. 执行流程

工具类:SharedElementTransitionHelper

★20.转场动画_第1张图片

1. 为共享元素设置TransitionName

为两个场景想要共享的View设置setTransitionName()为相同可标识的字符串。

view.setTransitionName(/* 可标识字符串 */);

2. 手动设置共享元素

方法简介

  • setEnterSharedElementCallback()
    • 设置的是 A->B 时, B 的动画。
    • 同时也是 B->A 时, B 的动画,虽然动画是相反的,但是会自动做倒序处理,可以看 执行流程
    • BActivity,则是由Activity.makeSceneTransitionAnimation()触发的。若 BFragment,则是在Fragmentattached()detached()触发的。
  • setExitSharedElementCallback()
    • 设置的是 A->B 时, A 的动画。
    • 同时也是 B->A 时, A 的动画,虽然动画是相反的,但是会自动做倒序处理,可以看 执行流程
    • AActivity,则是由Activity.makeSceneTransitionAnimation()触发的。若 AFragment,则是在Fragmentattached()detached()触发的。

步骤

  1. 定义SharedElementCallback对象。重写SharedElementCallback.onMapSharedElements()
  2. Activity或者Fragment中调用setEnterSharedElementCallback()setExitSharedElementCallback(),重新设置SharedElementCallback对象以实现重新设置 共享元素

简单示例

背景
  • AB 均为Activity
  • B 返回 A 时, 共享元素 发生了变化。
A.java

必须调用子ActivitysetResult()才会调用父ActivityonActivityReenter()

// 在Activity.onActivityReenter()中设置
SharedElementTransitionHelper.setExitSharedElementCallbackOnce(this, new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List names, Map sharedElements) {
        View newSharedElement = /* 获取新共享元素 */;
        String newTransitionName = /* 获取新TransitionName */;
        names.clear();
        sharedElements.clear();
        names.add(newTransitionName);
        sharedElements.put(newTransitionName, newSharedElement);
    }
});
B.java

此处不要在Activity.supportFinishAfterTransition()中设置,而应该在Activity.finishAfterTransition()

// 在Activity.finishAfterTransition()中设置
// 必须调用setResult()才会调用父Activity的onActivityReenter()
setResult(RESULT_OK);
SharedElementTransitionHelper.setEnterSharedElementCallbackOnce(this, new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List names, Map sharedElements) {
        View newSharedElement = /* 获取新共享元素 */;
        String newTransitionName = /* 获取新TransitionName */;
        names.clear();
        sharedElements.clear();
        names.add(newTransitionName);
        sharedElements.put(newTransitionName, newSharedElement);
    }
});

4. 延迟和开始共享元素动画

  • 用于处理在 转场动画 开始时, 共享元素 尚未加载的情况,此时延迟 转场动画 ,直至 共享元素 加载完毕。当有多个 共享元素 的时候也要确保所有这些 共享元素 全部加载完毕。
  • 在场景刚刚开始的地方,如Activity.onCreate()
    SharedElementTransitionHelper.pauseEnterTranstion(/* Activity */);
    
  • 在能访问到 共享元素共享元素 加载完毕的地方,如SharedElementTransitionHelper中监听了ViewPreDraw 阶段:
    SharedElementTransitionHelper.startEnterTranstionWhenViewIsReady(getActivity(), /* 共享元素 */);
    

5. 启动Activity

ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), view, view.getTransitionName());
startActivity(intent, optionsCompat.toBundle());

监听共享元素动画

getWindow().getSharedElementExitTransition().addListener(/* SharedElementTransitionHelper#TransitionListenerAdapter */);
getWindow().getSharedElementEnterTransition().addListener(/* SharedElementTransitionHelper#TransitionListenerAdapter */);

重叠属性

控制两个场景是否允许重叠。

代码

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);

效果

★20.转场动画_第2张图片
★20.转场动画_第3张图片

自定义共享元素动画【Todo】


    
    
    
    
    

  • 路径:changeBounds
  • 大小、缩放:changeTransform
  • 图片矩阵变换:ChangeImageTransform
  • 裁剪区域:ChangeClipBounds

教程

Material-Animations
Android Transition Framework
Postponed Shared Element Transitions

旧版转场动画【Todo】

你可能感兴趣的:(★20.转场动画)