Android场景动画(Scene)

一 概述


       Scene 是Android 19 引入的转换框架中一个场景api,帮我们友好的创建开始布局Scene和结束布局Scene,有了开始Scene和结束Scene,运用Transition框架来实现带有动画的场景切换。举个例子,从A布局切换到B布局,一般情况下处理是View.GONE,View.VISIBLE,但是这样太生硬了,没有一点过度效果。那么Android的Transition框架就可以完美的解决切换场景带来的生硬视觉感受。

其中Scene是一个容器,就是放置你定义的布局,而真正去做场景之间切换这个动作是Transition框架中TransitionManager 调用其中go方法或者transitionTo方法完成场景之间切换,而真正创建具体动画交由Transition子类来完成,开始动画交给Transition来执行。

二 使用

image

       这里一共有4个场景,这里先说前3个。每次切换都带有移动效果。在切换到第三个场景时,单独给第三个场景中TextView添加了淡入和淡出动画效果。
那么如果让我们去实现这样一个场景切换,可能会想到在一个布局中给不同的元素设置不同的动画,还得监听每个动画完成后显示第二个场景中的元素。这样写出来很难阅读和维护,如果再加一个元素,又得监听以及显示和隐藏。

       那么如何实现前三个场景切换呢?其实这三个场景对应三个layout.xml。每一个layout.xml对应一个Scene,各自之间不耦合,至于具体动画创建和偏移计算交给Transition子类来处理,TransitionManager 只是用于做控制流程。

还是看看官方提供的流程图

上述中gif图代码如下:

创建布局文件



    
        
        
        
        
    

    
        
    



    <矩形
     android:id="@+id/transition_square"
    />
    <箭头
     android:id="@+id/transition_image"
     android:layout_below="@id/transition_square"
    />
    <圆形
     android:id="@+id/transition_oval"
     android:layout_below="@id/transition_image"
    />




    

    

    

场景三布局文件这里省略。其实布局一样只是各个View中的位置不一样,场景三多了一个TextView,这里暂且先说场景一和场景二。从场景一和场景二可以发现两个场景id是一样的,只是位置不一样。其实场景切换中匹配规则除了id匹配还有如下匹配规则:

  • instance 匹配同一引用
  • transitionName 匹配同一transitionName
  • itemId 匹配ListView中adapter id

接下来就是代码创建Scene,调用TransitionManager.go()方法开启场景切换。

创建场景Scene1

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

创建场景Scene2

Scene mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, getActivity());

开始动画

...
case R.id.select_scene_1: {
    TransitionManager.go(mScene1);
    break;
}
case R.id.select_scene_2: {
    TransitionManager.go(mScene2);
    break;
}
...

这样就实现上面gif图场景一到场景二切换的动画了,场景三这里同理。上面给出创建场景,有两种方式。

  • 从当前位置创建Scene
  • 通过Scene.getSceneForLayout()方法创建Scene

Scene.getSceneForLayout() 参数介绍如下:

  • sceneRoot 表示从什么地方开始切换场景。
  • layoutId 切换到什么场景的布局文件,比如上述例子中生成Scene2实例,layoutId 就是场景2中的布局。
  • context 上下文

上述简单的例子是通过TransitionManager.go()触发动画,go()方法中其实设置了默认转换动画

    private void init() {
        setOrdering(ORDERING_SEQUENTIAL);
        addTransition(new Fade(Fade.OUT)).
                addTransition(new ChangeBounds()).
                addTransition(new Fade(Fade.IN));
    }

设置了一个changeBounds,和Fade转换效果。

类似于ChangeBounds类的还有以下几种,他们都是继承Transiton类

  • ChangeBounds检测view的位置边界创建移动和缩放动画
  • ChangeTransform检测view的scale和rotation创建缩放和旋转动画
  • ChangeClipBounds检测view的剪切区域的位置边界,和ChangeBounds类似。不过ChangeBounds针对的是view而ChangeClipBounds针对的是view的剪切区域(setClipBound(Rect rect) 中的rect)。如果没有设置则没有动画效果
  • ChangeImageTransform检测ImageView(这里是专指ImageView)的尺寸,位置以及ScaleType,并创建相应动画。
  • Fade,Slide,Explode这三个都是根据view的visibility的不同分别创建渐入,滑动,爆炸动画。

上述通过代码实现场景转换动画,下面通过xml方式定义一组动画集合。在res/transition/创建xml文件


    
    

xml使用

transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_fadein_together);

isSwitch = !isSwitch;
              TransitionManager.go(isSwitch ? scene2 : scene1, transition);

前面说了场景一和场景二的创建和使用,那么场景三也就so easy 了,可以发现场景三中多一个TextView,此TextView需要在切换场景时,淡入淡出,怎么实现呢?
其实可以通过TransitionInflater 中inflateTransitionManager方法实现。

  • 定义scene3_transition_manager.xml,
    指定场景布局文件,和切换动画。

    

  • 定义changebounds_fadein_together.xml,
    指定动画效果和需要新加入的Viewid。此时为场景三中新加入TextView Id

    
    
        
            
        
    

其中定义的target标签 targetId 表示只针对于这个id,还有和它相反的
excludeId 除了这个id。(excludeId 仅在api21上才可以)

创建场景Scene3

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

加载xml中定义的转换效果

TransitionManager mTransitionManagerForScene3 =TransitionInflater.from(getActivity())
.inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot)

调用

 mTransitionManagerForScene3.transitionTo(mScene3)
 

上述例子中是针对多个场景切换实现转换动画,
那么有时候没有多个场景切换,只想改变其中某一个场景下的某一个View属性来实现过度动画,
可以使用一下api。

TransitionManager.beginDelayedTransition(ViewGroup sceneRoot)

beginDelayedTransition原理是通过代码改变view的属性,然后通过之前介绍的ChangeBounds等类分析start scene和end Scene不同来创建动画。
以下例子实现在一个场景中特定的View放大效果,其实也是上面gif图的场景4了,具体效果,文末给出代码链接,可自行查看。

TransitionManager.beginDelayedTransition(mSceneRoot);
View square = mSceneRoot.findViewById(R.id.transition_square);
ViewGroup.LayoutParams params = square.getLayoutParams();
int newSize=getResources().getDimensionPixelSize(R.dimen.square_size_expanded);
params.width = newSize;
params.height = newSize;
square.setLayoutParams(params);

三 原理

       就拿上述中定义的两个场景Scene1Scene2来说, Scene 只是用来保存当前场景布局,而真正去创建动画和开始动画的是Transition,TransitionManager 只是用来做控制流程的。

假设
Scene1-->Scene2
当代码调用TransitionManager.go(mScene2)时执行流程

       从设置的mSceneRoot 开始,遍历Scene1中视图树,存储每次遍历的View在自己父View中的位置,以及该View中的id作为开始动画时位置和条件,当保存完毕,删除mSceneRoot 中存在的Scene1添加Scene2监听视图树的绘制,当绘制完毕遍历当前已添加的Scene2中视图树,存储每次遍历的View在自己父View中的位置,以及该View中的id作为结束动画位置和条件。当动画的开始位置和动画结束位置已确定,那么创建动画交给Transition子类,上述例子中用的是Transition的子类 ChangeBounds 。当通过结束位置和开始位置创建动画完毕后,最终通过TransitionrunAnimators开启动画。

参考链接:

googleDeveloper

BasicTransition

你可能感兴趣的:(Android场景动画(Scene))