Android过渡动画(Transition Animation)
Posted ZhangJianIsAStark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android过渡动画(Transition Animation)相关的知识,希望对你有一定的参考价值。
本篇博客记录一下android中过渡动画的用法。
过渡动画是Android 4.4引入的新的动画框架,它本质上仍是属性动画,
但对属性动画做了一层封装,以方便开发者实现Activity或者View的过渡动画效果。
和属性动画相比,过渡动画最大的不同是需要为动画前后准备不同的布局,
并通过对应的API实现两个布局的之间过渡动画。
不过相对而言,过渡动画比较吃内存,据传闻传闻,
在针对低内存设备的Android Go版本中,部分过渡动画会被砍掉。
一、简单示例
我们先来看看Transition动画的简单使用示例。
如下图所示,我们的Activity中定义了3个ImageView。
现在,我们准备将这些试图顺时针旋转一下。
如果使用补间动画或属性动画,为了实现这个需求,
我们需要写3个位置移动动画,然后分别应用到这三个View上面。
若使用过渡动画,整个实现过程则会简单的多。
准备工作
我们首先定义一下Activity对应的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/begin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始动画"/>
<!--做动画的父布局-->
<FrameLayout
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--scene1为动画的初始布局-->
<include layout="@layout/scene1"/>
</FrameLayout>
</LinearLayout>
定义启始与结束帧
在layout目录下定义初始时的视图布局文件scene1.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image1"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_1"
android:layout_centerInParent="true"/>
<ImageView
android:id="@+id/image2"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_2"
android:layout_below="@id/image1"
android:layout_alignParentLeft="true"/>
<ImageView
android:id="@+id/image3"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_3"
android:layout_below="@id/image1"
android:layout_alignParentRight="true"/>
</RelativeLayout>
然后,我们还需要定义结束时的视图布局文件scene2.xml,
注意视图中View的id与scene1.xml中一一对应:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image3"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_3"
android:layout_centerInParent="true"/>
<ImageView
android:id="@+id/image1"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_1"
android:layout_below="@id/image3"
android:layout_alignParentLeft="true"/>
<ImageView
android:id="@+id/image2"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/p_2"
android:layout_below="@id/image3"
android:layout_alignParentRight="true"/>
</RelativeLayout>
定义过渡效果
在过渡动画框架中,过渡效果均是继承Transition类。
框架内置了一些常用的效果,比如幻灯片Slide,淡入淡出 Fade等。
由于我们这里3个View涉及的都是位置的变化,
我们可以直接使用框架内置的动画效果ChangeBounds(实际上是变换视图边界的坐标)。
实际的使用
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ViewGroup rootView = findViewById(R.id.rootView);
Button button = findViewById(R.id.begin);
button.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
//定义结束Scene
Scene scene2 = Scene.getSceneForLayout(rootView, R.layout.scene2, MainActivity.this);
//利用TransitionManager进行变换
TransitionManager.go(scene2, new ChangeBounds());
);
至此,我们已经可以看出过渡动画的基本使用方式。
如上面的示意图所示:
首先,利用布局文件定义了一个视图树的两种状态(类似于开始帧和结束帧),这些状态被称为Scene。
Scene定义了页面的当前状态信息,Scene的实例化一般通过静态工厂方法实现:
public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
.............
然后,选定需要使用的动画效果,例如ChangeBounds等,
它能够处理View本身大小、位置改变,描述这些变化效果的对象就被成为Transition。
Transition定义了界面之间切换的动画信息,在使用TransitionManager时,
如果没有指定使用哪个Transition,那么会使用默认的AutoTransition。
AutoTranstion的效果就是先隐藏,后移动,最后显示:
public class AutoTransition extends TransitionSet
...........
private void init()
setOrdering(ORDERING_SEQUENTIAL);
addTransition(new Fade(Fade.OUT)).
addTransition(new ChangeBounds()).
addTransition(new Fade(Fade.IN));
最后,通过TransitionManager的接口,开启整个视图的动画转换即可。
TransitionManager就是控制Scene之间切换的控制器。
二、场景Scene
从前文的代码我们知道,Scene记录了一个视图树中所有View的属性值。
前面我们使用如下接口构造Scene:
public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
.............
除此之外,我们也可以利用构造函数直接创建Scene:
public Scene(ViewGroup sceneRoot, View layout)
......
可以看到,无论使用哪种方式创建Scene,都必须指定sceneRoot。
sceneRoot就是Scene对应视图树的根View。
当过渡动画开始时,sceneRoot中与初始Scene有关的View都会被remove,
即初始Scene中的View必须都是sceneRoot的子View;
在动画的结束时,sceneRoot重新添加结束Scene相关的View,
即结束Scene中的View必须都是sceneRoot的子View,或没有ParentView的View,
否则addView时会报错。
三、过渡Transition
Transition针对初始和结束场景对应视图树中View的属性,
例如width、height、position等,定义了变化时的过度效果。
Android目前内置了ChangeBounds、Fade等。
3.1、指定目标
前面示例中,Transition直接作用在整个视图树,
即对视图树上所有的View均生效。
如果不想将动画效果应用到所有的View上,
可以利用Transition的addTarget或removeTarget指定生效的View。
例如前面的例子,可以修改为:
.................
Scene scene2 = Scene.getSceneForLayout(rootView,
R.layout.scene2, MainActivity.this);
Transition transition = new ChangeBounds();
transition.addTarget(R.id.image1);
TransitionManager.go(scene2, transition);
.................
此时,只有image1对应视图在变化时有动画效果。
3.2、延迟动画
之前已经提到过,过渡动画实际上也是一种属性动画。
这一点从TransitionManager提供的延迟动画接口就可以看出来。
使用延迟动画接口时,不需要定义结束帧对应的Scene。
仍然使用前文的示例代码:
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(3000);
//开启延迟动画
TransitionManager.beginDelayedTransition(rootView, changeBounds);
//可以直接修改rootView中子View的属性
//当动画结束后,子View的属性就会被修改到指定值
View image1 = findViewById(R.id.image1);
ViewGroup.LayoutParams params1 = image1.getLayoutParams();
params1.height = 200;
params1.width = 200;
image1.setLayoutParams(params1);
View image2 = findViewById(R.id.image2);
ViewGroup.LayoutParams params2 = image2.getLayoutParams();
params2.height = 400;
params2.width = 400;
image2.setLayoutParams(params2);
当TransitionManager调用beginDelayedTransition后,系统会保存当前视图树的状态;
接着在代码中,我们修改了子View的属性;
在下一次绘制时,系统会比对视图树当前的状态和之前保存的状态,
然后系统就会运行过渡动画,并按照新的状态绘制View。
3.3、自定义Transition
这部分我们来看看如何自定义Transition,直接上例子:
//我定义了一个起始和结束时,View的高度发生变化时,就会旋转的动画效果
@TargetApi(19)
private class RotateWhenHeightChangeTransition extends Transition
private static final String PROP_NAME_TRANSITION_HEIGHT = "zhang:jian:just:for:test";
//首先必须复写captureStartValues和captureEndValues
//如同函数名,这里主要记录我们感兴趣的数据
//按照键值对的形式存入transitionValues.values
//记录开始Scene中View的高度
@Override
public void captureStartValues(TransitionValues transitionValues)
transitionValues.values.put(
PROP_NAME_TRANSITION_HEIGHT, transitionValues.view.getLayoutParams().height);
//记录结束Scene中View的高度
@Override
public void captureEndValues(TransitionValues transitionValues)
transitionValues.values.put(
PROP_NAME_TRANSITION_HEIGHT, transitionValues.view.getLayoutParams().height);
@Override
public Animator createAnimator(ViewGroup sceneRoot,
TransitionValues startValues, TransitionValues endValues)
if (startValues == null || endValues == null)
return null;
//比对开始和结束时,高度是否发生变化
int startHeight = (int)startValues.values.get(PROP_NAME_TRANSITION_HEIGHT);
int endHeight = (int) endValues.values.get(PROP_NAME_TRANSITION_HEIGHT);
//若发生变化,则创建属性动画对应的Animator
if (startHeight != endHeight)
return ObjectAnimator
.ofFloat(endValues.view, "rotation", 0, 360)
.setDuration(2000);
return null;
定义好后,就可以像系统内置的Transition那样,使用RotateWhenHeightChangeTransition了:
.......
//更改一下scene2对应的View的高度
//可以发现高度变化的View才会有旋转动画
Scene scene2 = Scene.getSceneForLayout(rootView,
R.layout.scene2, MainActivity.this);
TransitionManager.go(scene2, new RotateWhenHeightChangeTransition());
........
四、总结
至此,我们已经明白过渡动画的使用和自定义,
应该可以满足一般的工作需求。
以上是关于Android过渡动画(Transition Animation)的主要内容,如果未能解决你的问题,请参考以下文章
java 活动过渡动画,如Vine Android应用程序。 - 请参阅:http://blog.quent.in/index.php/2013/06/activity-transition-
xml 活动过渡动画,如Vine Android应用程序。 - 请参阅:http://blog.quent.in/index.php/2013/06/activity-transition-