使用Transitions API为安卓应用创建动画
英文原文:http://cases.azoft.com/create-android-app-animations-using-transitions-api/
为安卓创建动画
虽然移动应用对动画的依赖相当普遍,但在安卓系统中开发者认为创建一个动画的过程一直都是一种挑战。ios早就提供了处理动画的实用工具,而安卓上帮助开发者提高工作效率的解决方案还相对较新。
这些动画工具大大的节省了程序员的时间。 使用这些工具实现 creating a variety of app animations 非常方便。和对不同屏幕应用动画不同,开发者可以使用Transition API制造所谓的“场景动画”,Transition API自动产生过渡效果,而这只是冰山一角。我相信这篇文章所要分享的知识对安卓开发者会非常有用。
Transitions API:是如何工作的?
即使在Android4.0上,也有一个解决动画问题的早期办法:针对ViewGroup的布局动画。但是这个工具不够灵活,开发者无法完全掌控动画的过程。不过从Android 4.4 KitKat开始,有了Transitions API,在兼容包中也有Transitions API,因此可以在几乎所有的安卓设备上使用。(这点我大大的怀疑,也许兼容包中是有Transitions API,但是只是做到不出错,兼容而已,根本没有动画效果,不知道新的兼容包是否有改进)。
在KitKat Transition API中,出现了诸如场景(Scene)、场景过渡(Transition)这样的概念,为了决定root layout,场景Scene root被引入,场景中的所有变化都发生在Scene root中。同时,场景自身本质上是对ViewGroup的一层封装,描述了自己以及所有View对象的的状态。而过渡(Transition)则是一种这样的机制:读取不同场景之间View属性的变化,从而产生让这种变化看起来平滑的动画。
KitKat中Transition框架的Transition API为创建动画提供了如下特色:
组级别的动画:可以将整个View树作为整体动画,你只需指定ViewGroup,它的各个元素就会自动应用动画。
基于Transition的动画
内置动画:内置简单的动画,比如dissolution,darkening,resizing,movement等等。
对资源文件的支持:你可以不必写代码,在资源文件中创建动画。
回调:提供掌控动画过程的所有必要的回调方法。
原文此处有youtube视频
虽然有如此多的优点。但是这个新的api也有一些局限:
在那些不在UI线程中工作的View,比如SurfaceView或者TextureView中使用的时候不流畅。
AdapterView,比如ListView,当你需要针对列表中某个单独的元素使用动画的时候。
偶尔会在resize TextView的时候会出现同步的问题:在另一个对象的改变结束的之前,字体会提前出现在下一个场景中。
但是,这些局限都不是很关大局。在实际开发中,需要在SurfaceView中应用动画的类似情况非常少见。
考虑下面Transition框架的动画例子:
通过资源文件创建一个场景:
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/master_layout">
<TextView
android:id="@+id/title"
...
android:text="Title"/>
<FrameLayout
android:id="@+id/scene_root">
<include layout="@layout/scene_first" />
</FrameLayout>
</LinearLayout>
res/layout/scene_first.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/text_view1
android:text="Text Line 1" />
<TextView
android:id="@+id/text_view2
android:text="Text Line 2" />
</RelativeLayout>
res/layout/scene_second.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/text_view2
android:text="Text Line 2" />
<TextView
android:id="@+id/text_view1
android:text="Text Line 1" />
</RelativeLayout>
Scene mFirstScene;
Scene mSecondScene;
// Create the scene root for the scenes in this app
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
// Create the scenes
mFirstScene =
Scene.getSceneForLayout(mSceneRoot, R.layout.scene_first, this);
mSecondScene =
Scene.getSceneForLayout(mSceneRoot, R.layout.scene_second, this);
通过代码创建场景:
// Obtain the scene root element
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered
mViewHierarchy = (ViewGroup)findViewById(R.id.scene_conteiner);
// Create a scene
Scene scene = new Scene(mSceneRoot, mViewHierarchy);
通过资源文件创建Transition:
res/transition/fade_transition.xml
<fade xmlns:android="http://schemas.android.com/apk/res/android" />
Transition mFadeTransition =
TransitionInflater.from(this).
inflateTransition(R.transition.fade_transition);
也可以完全通过代码创建:
Transition mFadeTransition = new Fade();
你还可以创建整个动画集。比如:move,resize,darken无缝的结合在一起:
在资源文件中:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="sequential">
<fade android:fadingMode="fade_out" />
<changeBounds />
<fade android:fadingMode="fade_in" />
</transitionSet>
在代码中:
TransitionSet set = new TransitionSet();
set.addTransition(new Fade())
.addTransition(new ChangeBounds())
.addTransition(new AutoTransition());
如果需要,还可以将动画应用到某个View对象,而不是整个场景:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds />
<fade android:fadingMode="fade_in" />
<targets>
<target android:targetId="@id/transition_title" />
</targets>
</fade>
</transitionSet>
只需一行代码就可以创建Transition Manager。需要将所有的场景和Transition都写在一个地方。Transition Manager提高了工作速度和掌控动画过程的效率。
res/transition/transition_manager.xml
<transitionManager xmlns:app="http://schemas.android.com/apk/res-auto">
<transition
app:fromScene="@layout/scene_reg1"
app:toScene="@layout/scene_reg2"
app:transition="@transition/trans_reg1_to_reg2" />
<transition
app:fromScene="@layout/scene_reg2"
app:toScene="@layout/scene_reg3"
app:transition="@transition/trans_reg2_to_reg3" />
...
</transitionManager>
如何运行场景? 简单!
使用自定义的Transition:
mTransitionManager.transitionTo(scene);
或者
TransitionManager.go(scene, fadeTransition);
使用默认的Transition:
TransitionManager.go(scene);
或者根本没有Transition:
scene.enter();
你也可以无需创建场景就使用Transition:
res/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="@+id/inputText"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
</RelativeLayout>
MainActivity.java
private TextView mLabelText;
private Fade mFade;
private ViewGroup mRootView;
// Load the layout
setContentView(R.layout.activity_main);
// Create a new TextView and set some View properties
mLabelText = new TextView();
mLabelText.setText("Label").setId("1");
// Get the root view and create a transition
mRootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(IN);
// Start recording changes to the view hierarchy
TransitionManager.beginDelayedTransition(mRootView, mFade);
// Add the new TextView to the view hierarchy
mRootView.addView(mLabelText);
// When the system redraws the screen to show this update,
// the framework will animate the addition as a fade in
使用TransitionListener的接口,你可以控制每个动画元素的执行过程:
public static interface TransitionListener {
void onTransitionStart(Transition transition);
void onTransitionEnd(Transition transition);
void onTransitionCancel(Transition transition);
void onTransitionPause(Transition transition);
void onTransitionResume(Transition transition);
}
创建自己的动画,比如你可以改变View的背景颜色:
原文此处有youtube视频
public class ChangeColor extends Transition {
private static final String PROPNAME_BACKGROUND =
"customtransition:change_color:background";
private void captureValues(TransitionValues values) {
values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot,
TransitionValues startValues,
TransitionValues endValues) {
if (null == startValues || null == endValues) {
return null;
}
final View view = endValues.view;
Drawable startBackground =
(Drawable) startValues.values.get(PROPNAME_BACKGROUND);
Drawable endBackground =
(Drawable) endValues.values.get(PROPNAME_BACKGROUND);
ColorDrawable startColor = (ColorDrawable) startBackground;
ColorDrawable endColor = (ColorDrawable) endBackground;
if (startColor.getColor() == endColor.getColor()) {
return null;
}
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(),
startColor.getColor(), endColor.getColor());
animator
.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Object value = animation.getAnimatedValue();
if (null != value) {
view.setBackgroundColor((Integer) value);
}
}
});
return animator;
}
}
自动产生中间值,这就是为什么我们的例子中颜色逐渐从红色变到蓝色。这个方法打开了创建多种自定义动画与过渡的可能性:只有想不到,没有做不到。
为什么我们要关心Transition?
关于创建动画的快速与简便请了解development of mobile apps。Azoft团队对Transitions API非常热情,我们已经在项目中使用了这个方法。使用Scene创建动画节省了很多时间和精力,这对开发者和急于看到结果的客户来说都是好事。
告诉我们关于你为安卓创建动画的经历。你们使用Transitions API吗?除此之外你们还使用什么工具来为移动应用创建动画?