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图代码如下:

创建布局文件

<!--默认布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    >
    <RadioGroup
      ...>
        <RadioButton
           android:id="@+id/select_scene_1"
           ...
           >
        <RadioButton
           android:id="@+id/select_scene_2"
        ...>
        <RadioButton
           android:id="@+id/select_scene_3"
        ...
        >
        <RadioButton
           android:id="@+id/select_scene_4"
        ...>
    </RadioGroup>

    <FrameLayout
        android:id="@+id/scene_root"
        ...
      >
        <include layout="@layout/scene1"/>
    </FrameLayout>
</LinearLayout>
<!--场景一 布局文件-->
<RelativeLayout
    android:id="@+id/container"
    ...>
    <矩形
     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"
    />
</RelativeLayout>
<!--场景二 布局文件-->
<RelativeLayout
    android:id="@+id/container"
    ...
    >

    <View
        android:id="@+id/transition_square"
        android:layout_alignParentBottom="true"/>

    <ImageView
        android:id="@+id/transition_image"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"/>

    <ImageView
        android:id="@+id/transition_oval"
        android:layout_centerHorizontal="true"/>
</RelativeLayout>

场景三布局文件这里省略。其实布局一样只是各个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文件

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds />
    <fade android:fadingMode="fade_in_out"/>
</transitionSet>

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,
    指定场景布局文件,和切换动画。
<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,
    指定动画效果和需要新加入的Viewid。此时为场景三中新加入TextView Id
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds />
    <fade android:fadingMode="fade_in_out">
        <targets>
            <target android:targetId="@id/transition_title" />
        </targets>
    </fade>
</transitionSet>
其中定义的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

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

推荐阅读更多精彩内容