本文将介绍Material Design中的场景动画。
所谓的场景,可以理解为当前布局的一个样式,而场景动画是由一个布局切换到另一个布局的动画效果。在这两个布局中,若有同样id的控件,那么将这两个布局进行以场景动画的方式切换时,系统会比较两个相同的id的控件的相对位置,并对切换过程执行属性动画。
可由如下示意图对场景动画的切换窥知一二。
将Starting Layout作为开始场景(Starting Scene)的布局,将Ending Layout作为结束场景(Ending Scene)的布局,通过Transition Manager管理需要相互切换的两个布局,而底层实现是属性动画。
下面将通过一个demo浅析场景动画。
效果展示
界面首先展示了一个“Hello world!”和一个“TRANSFORMATION”按钮,点击按钮,经过场景动画,切换到第二张图,按钮移到了底部,“Hello world!”平移到了中间顶部,同时多了一个红色的圆和中间的“Hello world!”。实现这个动画效果只需要按照上述流程即可。下面首先展示XML布局。
XML布局
Scene1的XML布局
首先是Scene1的布局,为简便起见,直接为button设置android:onClick属性,以便点击该button时,直接执行相应方法。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textView">
<Button
android:id="@+id/goButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="goToScene2"
android:text="@string/button_go" />
</LinearLayout>
</RelativeLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
Scene2的XML布局
值得注意的是,第一个TextView和Button的id与Scene1中的TextView、Button的id是一模一样的,这也是场景动画最关键的一点,即需要执行动画变换的控件需要在起点与终点的布局场景中设置相同的id。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.bignerdranch.android.transitionexample.TransitionFragment">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/hello_world" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/hello_world" />
<LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/circle_red" />
<Button
android:id="@+id/goButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="goToScene1"
android:text="@string/button_go" />
</LinearLayout>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
容器XML布局
除了需要两个场景的布局之外,还需要一个容器布局装载场景。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/fragment_transition_scene_1"></include>
</FrameLayout>
需要为该容器设置一个id,以便在代码中使用。
activity逻辑实现
对照场景动画的实现流程图,可以编写如下代码,便能方便地实现场景动画。
public class MainActivity extends Activity {
private Scene mScene1;
private Scene mScene2;
private AutoTransition autoTransition;
private AutoTransition autoTransition2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
ViewGroup container = (ViewGroup) findViewById(R.id.container);
mScene1 = Scene.getSceneForLayout(container, R.layout.fragment_transition_scene_1, this);
mScene2 = Scene.getSceneForLayout(container, R.layout.fragment_transition_scene_2, this);
autoTransition = new AutoTransition();
autoTransition.setInterpolator(new EaseBackInInterpolator());
autoTransition.setDuration(1000);
autoTransition2 = new AutoTransition();
autoTransition2.setInterpolator(new EaseQuadInInterpolator());
autoTransition2.setDuration(500);
}
public void goToScene1(View view) {
TransitionManager.go(mScene1, autoTransition);
}
public void goToScene2(View view) {
TransitionManager.go(mScene2, autoTransition2);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
补充 :用XML设置动画切换实现场景动画
前面使用代码的方式动态实现场景动画,下面将使用XML的方式实现场景动画,首先定义切换动画的XML。
场景切换动画
实现场景动画的动画效果主要是依靠抽象类Transition,它的实现类可以实现不同的动画效果。
abstract class Transition的实现类:
- ChangeBounds (大小变化、位置变化)
- ChangeClipBounds (改变剪切区域大小)
- ChangeImageTransform (改变图形矩阵)
- ChangeScroll (改变滚动方式)
- ChangeTransform (改变转换方式)
- TransitionSet (动画集合)
- Visibility (可见性-元素的出现与消失)
–Explode (元素以爆炸方式出现或消失)
–Fade (元素淡入淡出)
–Slide (元素飞入飞出)
下面以动画集合TransitionSet为根标签设置一组动画。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="sequential">
<fade
android:duration="1000"
android:fadingMode="fade_out" />
<changeBounds
android:duration="2000"
android:interpolator="@android:interpolator/bounce" />
<fade
android:duration="1000"
android:fadingMode="fade_in" />
</transitionSet>
其中属性android:transitionOrdering=”sequential”表示按顺序执行动画。
同理,还可以继续定制其他动画效果。如下所示:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="sequential">
<fade
android:duration="500"
android:fadingMode="fade_out" />
<changeBounds
android:duration="800" android:interpolator="@android:interpolator/anticipate_overshoot" />
<fade
android:duration="800"
android:fadingMode="fade_in" />
</transitionSet>
上述动画执行效果为anticipate_overshoot样式。
设置完不同的动画样式集后,需要把这些集合装入到TransitionManager中,如下所示:
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
<transition
android:fromScene="@layout/fragment_transition_scene_1"
android:toScene="@layout/fragment_transition_scene_2"
android:transition="@transition/fast_auto_transition" />
<transition
android:fromScene="@layout/fragment_transition_scene_2"
android:toScene="@layout/fragment_transition_scene_1"
android:transition="@transition/slow_auto_transition" />
</transitionManager>
activity逻辑实现
public class MainActivity extends Activity {
private TransitionManager mTransitionManager;
private Scene mScene1;
private Scene mScene2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
ViewGroup container = (ViewGroup) findViewById(R.id.container);
TransitionInflater transitionInflater = TransitionInflater.from(this);
mTransitionManager = transitionInflater.inflateTransitionManager(R.transition.transition_manager, container);
mScene1 = Scene.getSceneForLayout(container, R.layout.fragment_transition_scene_1, this);
mScene2 = Scene.getSceneForLayout(container, R.layout.fragment_transition_scene_2, this);
}
public void goToScene1(View view) {
mTransitionManager.transitionTo(mScene1);
}
public void goToScene2(View view) {
mTransitionManager.transitionTo(mScene2);
}
}