Android 转场动画

转场动画

转场动画: 是Android L 引入的动画效果, 可以说是api19引入的场景(Scene)动画的扩展. 使开发者更加方便的实现布局(界面)变化时候的过渡动画.

Android L 是Google于2014年升级的系统版本号, 在2015年国内厂商新机就开始推送Android L, 现在是2017年我觉得此时不用更待何时.

转场动画(Transition)

在android.transition包下提供关于transitionAnimation的过渡框架, Transiton框架是在api19引入, 但是转场动画却是在api21引入.

Tip: 某些动画效果可能需要api23之上

类关系:

  • Transition
    • ChangeBounds 改变目标视图的布局边界
    • ChangeClipBounds 裁剪目标视图边界
    • ChangeTransform 改变目标视图的缩放比例和旋转角度
    • ChangeImageTransform 改变目标图片的大小和缩放比例
    • ChangeScroll
    • TransitionSet
      • AutoTransition 默认过渡动画
    • Visibility 其子类都属于界面切换动画
      • Explode 爆炸
      • Fade 淡出
      • Slide 上下滑动

Transition是一个不需要考虑关键帧(keyFrame)只需要告诉系统你想要的动画效果就能实现其过渡

带有change前缀的Transition的子类只是针对特有属性才有效果

而Visibility的子类是针对共享元素的坐标创建动画效果

简单的效果实现演示:

界面A

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.button) Button mButton;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.button) public void onClick() {
        startActivity(new Intent(this, SecondActivity.class), ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
    }
}

界面B

扫描二维码关注公众号,回复: 1231368 查看本文章
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);

        getWindow().setExitTransition(new Slide());
        getWindow().setEnterTransition(new Slide());
    }
}

Tip: 官方文档说明转场动画要求当前Activity必须在setContentView之前写入如下代码, 不过api21以上并不需要以下设置.

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

如果要退出界面仍然有transition动画不能执行finish, 需要执行finishAfterTransition()

动画目标

默认转场动画会对所有的子View进行遍历加载动画, 但是如果添加目标则不会进行遍历所有子View, 或者你也可以排除特定View.

对于目标有三个操作

  • 添加

    默认会进行遍历所有的视图加载动画, 但是如果使用了添加就不会遍历所有, 只会让指定的视图进行动画

  • 排除

    如果使用排除方法, 依旧会进行遍历视图对象, 不过会排除你指定的视图

  • 删除

    删除目标是在动画已经遍历视图完成以后还想对目标集合进行变更, 就可以删除指定的视图

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

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

Transition addTarget (String targetName)

Transition addTarget (Class targetType)

Transition addTarget (int targetId)

删除是removeTarget(), 排除是excludeTarget()

转场动画都支持设置监听器

Transition addListener (Transition.TransitionListener listener)

TransitionListener和AnimatorListener一样的重写方法.

窗口(Window)

从示例可以看出转场动画在代码中是通过获取Window对象进行设置的. 看下Window有哪些关于转场动画的方法.

转场动画有四种场景:

// 当前界面进入动画
void setEnterTransition (Transition transition)
// 当前界面退出动画
void setExitTransition (Transition transition)
  
// 以下介绍的是返回时的动画, 如果不设置就会默认和进入和退出动画相同
  
// 下个界面返回当前界面时, 当前界面进入动画
void setReenterTransition (Transition transition)
  
// 返回上个界面时当前界面退出动画
void setReturnTransition (Transition transition)

默认情况下界面A的退出动画还没有结束时, 界面B的进入动画就会开始执行. 以下两个方法默认为true. 想要进入动画等待退出动画结束后再播放就需要以下两个方法设置为false.

建议处于默认开启的状态, 否则可能出现背景空白期. 如果想清晰的看出几个不同状态的动画顺序可以开启

void setAllowEnterTransitionOverlap (boolean allow)
void setAllowReturnTransitionOverlap (boolean allow)

直接设置一个

void setTransitionManager (TransitionManager tm)

在分享元素过渡的时候是否允许重叠

void setSharedElementsUseOverlay (boolean sharedElementsUseOverlay)

Tip: 转场动画还支持主题文件里面直接设置

界面选项(ActivityOptions)

如果想兼容api16之前的系统版本可以使用ActivityOptionsCompat

该类用跳转界面的使用作为可选参数传递;

如果想让转场动画生效就必须使用下面两种方法开启界面

// 单一共享元素, 如果没有共享元素传入NULL
ActivityOptions makeSceneTransitionAnimation (Activity activity, 
                View sharedElement, 
                String sharedElementName)

// 支持多个共享元素
ActivityOptions makeSceneTransitionAnimation (Activity activity, 
                Pair...<View, String> sharedElements)

自定义进入和退出动画, 和overridePendingTransition方法一样

ActivityOptions makeCustomAnimation (Context context, 
                int enterResId, 
                int exitResId)

下面介绍三种系统提供的默认动画效果, 我实际使用感觉效果不是很明显, 感觉用处不大.

裁剪动画, 这是api23(Android m) 新增api.

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

缩放动画

ActivityOptions makeScaleUpAnimation (View source, 
                int startX, 
                int startY, 
                int width, 
                int height)

缩略图

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

场景(Scene)

场景可以理解为在一个界面(Activity)当中切换或者改变布局内容, 场景是在api19引入

创建方式有两种, 一种是通过以下的静态方法直接创建

Scene getSceneForLayout (ViewGroup sceneRoot, 
                int layoutId, 
                Context context)

或者通过构造方法

Scene (ViewGroup sceneRoot,  // 当前场景
                View layout) // 需要进入的新场景

sceneRoot可以称为根视图. 在官方示例中是当前界面布局的视图对象, 可以理解为其场景(Scene)依附的Activity容器

如果你需要动态的设置场景的视图内容, 可以只指定根视图

Scene (ViewGroup sceneRoot)

进入场景和退出场景, 不带任何动画. 一般情况下场景的进入由TransitionManager负责

void enter ()

void exit ()

exit并不会对场景有任何变化

在场景进入和退出时可以设置两个回调方法

void setEnterAction (Runnable action)

void setExitAction (Runnable action)

Tip: 如果两个场景存在相同的ID会自动进行共享元素动画(前提是用的不是默认的AutoTransiton, 因为该类没有使用Change)

过渡管理器(TransitionManager)

Scene默认是没有针对场景的变化, TransitionManager提供两种方式

  1. 这种默认的过渡动画是 AutoTransition

    static void go (Scene scene)
    

  2. 自定义过渡动画

    static void go (Scene scene, 
                    Transition transition)
    

开始延迟过渡, 即保存当前视图的属性状态, 然后在之后其发生改变的时候自动进行过渡动画

void beginDelayedTransition (ViewGroup sceneRoot)

void beginDelayedTransition (ViewGroup sceneRoot, 
                Transition transition)

后面还可以结束自动保存属性状态

void endTransitions (ViewGroup sceneRoot)

场景切换

// 创建一个自定义Transition动画的TransitionManager
mTransitionManagerForScene3 = TransitionInflater.from(getActivity())
.inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot);

// 然后切换任意布局
mTransitionManagerForScene3.transitionTo(mScene3);

该方法等同于提到的go()

void transitionTo (Scene scene)

XML定义

不同于普通的动画, 转场动画拥有专属的资源目录transition

在res/transition目录下创建XML文件

标签一览

引用XML

  Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);

或者可以直接在主题中设置

<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowEnterAnimation">@transition/explode</item>
<item name="android:windowReenterTransition">@transition/explode</item>

TransitionInflat除上面提到的方法外还可以填充trantionManager对象

TransitionManager inflateTransitionManager (int resource, 
                ViewGroup sceneRoot)

共享元素(ShareElement)

共享元素

一般是在关联的界面之前存在相同的(或者说类似的, 并不强制一定要相同)控件元素就会使用, 例如下图的按钮:

A界面跳转要指定共享元素

        startActivity(new Intent(this, SecondActivity.class),
                ActivityOptionsCompat.makeSceneTransitionAnimation(this, mButton, "button")
                        .toBundle());

B界面的布局文件中指定共享元素android:transitionName="button"

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个界面"
        android:transitionName="button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="99dp"
        />

可以看到所谓的共享元素即在两个界面的两个控件(或多个控件)之间的过渡变化效果

可以看到转场动画的操作都是针对Window对象, 拥有如下方法(同时都拥有对应的getter方法)

共享元素

void setSharedElementEnterTransition (Transition transition)
void setSharedElementExitTransition (Transition transition)
void setSharedElementReenterTransition (Transition transition)
void setSharedElementReturnTransition (Transition transition)

自定义Transiton

主要重写三个方法

  1. 开始值
  2. 结束值
  3. 创建动画

官方示例

Tip: 针对不同的View采用不同的动画效果可以重写Transition

扩散(Propagation)

Propagation可以指定Transition中的视图过渡延迟, 控制进入当前场景的视图进入的先后顺序. 例如要求Explode动画中特定的View速度快于其他的视图.

扩散中心

首先需要Transition确认扩散中心

void setEpicenterCallback (Transition.EpicenterCallback epicenterCallback)

在Transiton.EpicenterCallback回调中需要重写以下方法

Rect onGetEpicenter (Transition transition)

在回调方法内返回一个矩形, 其矩形的中心即扩散中心.

关系:

  • TransitionPropagation
    • VisibilityPropagation
      • SidePropagation
      • CircularPropagation

通过transition的方法指定

void setPropagation (TransitionPropagation transitionPropagation) // 设置扩散

void setPropagationSpeed (float propagationSpeed) // 设置速度

查看源码可以看到Explode默认使用的CircularPropagation

Slide使用的自然是SlidePropagation

TransitionPropagation 属于抽象类提供三个重写方法

void captureValues (TransitionValues transitionValues)

String[] getPropagationProperties ()

long getStartDelay (ViewGroup sceneRoot, 
                Transition transition, 
                TransitionValues startValues, 
                TransitionValues endValues)

分别控制:

  1. 捕捉

示例

slide.setPropagation(new VisibilityPropagation() {
  @Override public long getStartDelay(ViewGroup sceneRoot, Transition transition,
                                      TransitionValues startValues, TransitionValues endValues) {
    return 0;
  }
});

路径动作

PathMotion是在api21引入的过渡动画的一种新的实现方式. 可以通过指定路径来限制动画运动轨迹

通过Transition设置路径动画

void setPathMotion (PathMotion pathMotion)

PathMotion

  • ArcMotion (弧形动作)
  • PatternPathMotion

弧形轨迹

共享元素都是在不同的界面间的位置变化是以直线运动的, 通过指定以下路径动作可以变成弧形轨迹

参考 GooglePlay 的实现效果

<changeBounds>
   <arcMotion android:minimumHorizontalAngle="15"
              android:minimumVerticalAngle="0"
              android:maximumAngle="90"/>
 </changeBounds>

参数:

  • android:maximumAngle

    ​ 开始和结束的最大弧形角度

  • android:minimumHorizontalAngle

    ​ 两者接近水平的时候最小角度

  • android:minimumVerticalAngle

    ​ 垂直同上

路径动作

可以完全自定义路径来控制共享元素的运动轨迹

<changeBounds>
     <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
 </changeBounds>

Tip: patternPathData 的参数值类似矢量动画

自定义路径动作

 <changeBounds>
     <pathMotion class="my.app.transition.MyPathMotion"/>
 </changeBounds>

猜你喜欢

转载自juejin.im/post/5b0e8554f265da08ed7a15d1