Develop -- Training(十七) -- 动画视图的使用场景和转换

来源:互联网 发布:笨办法学python第五版 编辑:程序博客网 时间:2024/06/07 16:05

一个用户界面经常被改变是由于用户输入和其他事件。例如,一个活动界面包含了一个表单,用户可以在表单中搜索,当用户提交它并在表单中显示搜索结果列表。

在这些情况下提供了视觉的连续性,在用户界面上有不同层次视图的动画变化。这些动画给用户反馈他们的操作事件和帮助他们怎样使用这个app。

android包括转换框架,使你能够很容易的在两个视图层级之间进行动画改变。这个框架动画的视图可以在运行时刻改变他们的属性值。这个框架包括内置动画的一些共同效果,让你能够创建自定义的动画和转化在它的生命周期中。

这节课教你使用内置动画在视图层级发生改变的时候,这节课也包括如何创建自定义动画。

注意:对于android的版本早于4.4.2(API 19),但大于或等于android 4.0(API 14),使用animateLayoutChanges 属性作为动画布局。对于更多信息,请参见 Property Animation和Animating Layout Changes。

过渡框架

动画能够让你的应用程序界面更加有视觉上的吸引力,动画能够突出变化,提供视觉线索,帮助用户使用你的app。

为了帮助你在一个视图层次结构和另一个视图之间进行动画,android提供了过渡框架。这个框架可以应用一个或多个动画,在所有视图它们层级发生改变的时候。

这个框架有以下几个功能:

1.组级别动画

将一个或多个动画效果都应用在一个视图层次。

2.基于过渡的动画

运行动画基于视图属性值的开始和结束之间的改变。

3.内置动画

包括预定义的动画,常见的效果,如淡出或运动。

4.资源文件支持

加载视图层级结构和内置动画从资源文件中。

5.生命周期回掉

定义提供了动画和层次的变化过程更精细的控制回调。

1.概述

图1中的例子显示了一个动画如何提供视觉线索,帮助用户。当应用程序从它的搜索进入屏幕,到其搜索结果出现屏幕的变化,它会淡出不再使用的视图,并在新的视图中淡出。

这个例子动画使用了过渡框架。这个框架改变了所以的从第一个界面到第二个界面的动画。一个视图能够作为一个简单的单个视图,也能够作为一个复杂的视图组。这个框架的动画能够改变每一个视图一个或者多个属性值在视图开始一直到结束的过程中。

过渡框架与视图层次结构和动画是并行工作的。这个框架的目的是存储视图层级结构的状态,改变这些层级结构是为了设备屏幕外观,动画通过存储和应用动画定义的变化。

图2中的图表说明了视图层次结构、框架对象和动画之间的关系:

这里写图片描述

过渡框架提供了抽象化的场景,转换,转换管理。在下一节将详细描述。使用这个框架,你要创建视图层级结构的场景,在你的应用程序中药计划改变它们。下一步,你要创建你想要使用的过渡动画。动画在两个视图之间开始,你要使用一个转换管理来指定在结束场景使用哪个过渡。这个程序的详细描述在下面的课程。

2.场景

一个场景存储的视图的状态,包括所有的视图和它们的属性值。一个视图层级可能是一个简单的额视图,或者是一个混合的视图树和它的子布局。在一个场景中存储视图的状态,能够使你过渡到另一个场景的状态。这个框架提供了Scene类代表一个场景。

这个框架让你从布局的资源文件或者从代码的ViewGroup对象创建一个场景,如果你生成的视图层级是动态的,或者你在运行时刻去修改它,用代码创建场景是很有用的。

在大多数情况下,你不显示的创建一个起始场景。如果你要应用一个过渡,这个框架的使用提供了结束场景过后紧随的开始场景的过渡。如果你不应用这个过渡,这个框架收集了当前场景的状态的视图信息。

当你使一个场景变化的时候,一个场景也能够定义自己的行为。例如,在你过渡一个场景过后清理视图设置是很有用的。

除了视图层级和属性值,一个场景存储着父视图层级的引用。这个根视图被称作scene root。改变场景和动画,影响在场景的根发生的场景。

学习怎样创建场景,请看Creating a Scene。

3.过渡

在过渡框架中,动画创建是连续的帧来描述视图在开始和结束的变化。动画信息存储在Translation对象中,运行这个动画,你应用这个变化使用TransitionManager实例。这个框架能够让两个不同的场景转变,或者转变当前场景的不同状态。

这个框架包括一组内置转换的常用动画效果,像淡入淡出和改变视图大小。你也能够自定义一个过渡来创建一个动画效果,使用动画框架API。这个过渡框架也能够让你结合不同的动画效果在一个过渡中包含一组单独的内置或自定义过渡。

这个过渡的生命周期类似于Activity的生命周期,它代表着过渡状态在框架的监听器中一个动画的开始到完成。在重要的生命周期中,这个框架调用回调方法,你能够实现,然后调整在不同过渡时期的用户界面的UI。

关于过渡的更多信息,请查看Applying a Transition 和 Creating Custom Transitions。

4.限制

本节列出了过渡框架的一些已知的限制:

1.动画应用在一个SurfaceView上可能不会正确的出现。SurfaceView实例是在非UI线程中更新的,因此,更新可能与其他视图的动画不同步。

2.一些特别的过渡类型可能在textureview不会产生你所希望的动画效果。

3.集成自AdapterView,像ListView管理他们子视图的方法可能与过渡框架不兼容。如果你试图给一个基于AdapterView的视图添加动画,该设备可能会挂起。

4.如果你用动画来重置TextView的大小,在对象已经完全调整之前本文将弹出到一个新位置。为了避免这个问题,不要用动画来重置包含文本的视图大小。

创建一个场景

场景存储了视图层级的状态,包括所有的视图和属性值。过渡框架能够运行在一个场景的开始和结束之间。开始场景经常决定从当前用户界面自动开始。对于结束场景,框架让你能够从布局资源文件或者从你代码的一组视图创建场景。

这节课教你怎样创建一个场景,怎样定义一个场景的动作。下一节课教你怎样在场景之间过渡。

注意:框架能够让单个视图层级动画改变,而不使用场景,例如在Apply a Transition Without Scenes的描述中。然而,明白这节课是学习过渡的基础工作。

1.从布局资源创建场景

你能创建场景的实例直接从布局的资源文件。使用这个技巧的视图大多数是静态文件。这个结果场景就是你创建场景的实例表示着视图的状态。如果你改变视图,你必须重新创建场景,框架创建的场景从一个文件的整个视图,你也能够不创建场景从部分资源文件。

创建Scene实例从布局资源文件,重新得到场景的根布局作为一个ViewGroup实例,用Scene.getSceneForLayout()方法得到场景的根,资源文件ID包含了场景的视图层级。

2.定义场景的布局

下面的代码片段教你怎样创建两个不同的场景用相同的场景根元素,这个片段也展示了加载多个不相干的Scene对象,没有暗示他们是相互关联的。

这个例子包括如下的布局定义:

1.这个activity的主布局有一个TextView和子布局。
2.第一个场景是一个相对布局有两个TextView。
3.第二个场景也是相对布局,有两个相同的TextView以不同的顺序。

这个例子是故意这样的,使所有的动画都发生在子布局内的主布局的活动中。在主布局中的TextView仍然是静态的。

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/a_scene" />    </FrameLayout></LinearLayout>

这个布局定义包含了一个TextView和一个子布局作为场景的根布局,第一个场景的布局包含在主布局中。允许应用程序显示它作为初始用户界面,并加载到场景中,由于该框架可以只加载一个完整的布局文件到一个场景中。

res/layout/a_scene.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/another_scene.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>

3.从布局生成场景

在你定义两个相对布局过后,你能够获得场景的每一个元素。这使你能够在两个用户界面配置之间进行过渡。获取一个场景,你需要场景根和布局资源ID的引用。

下面的代码片段展示了获取场景根的引用,从资源文件创建两个Scene对象。

Scene mAScene;Scene mAnotherScene;// Create the scene root for the scenes in this appmSceneRoot = (ViewGroup) findViewById(R.id.scene_root);// Create the scenesmAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);mAnotherScene =    Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);

在应用程序中,在一个视图中有两个Scene对象,两个场景用在res/layout/ activity_main.xml的FrameLayout元素定义场景根。

4.从代码创建场景

你也能创建一个场景实例通过代码的ViewGroup对象。使用这个技巧,你能够在代码中直接修改视图,或者当你动态生成他们的时候。

创建场景从代码的视图层级中,使用Scene(sceneRoot, viewHierarchy)构造器。这个构造器等价Scene.getSceneForLayout() 方法,当你已经加载了一个布局文件。

下面的片段展示创建一个场景实例从场景根元素和场景的视图层级的代码中。

Scene mScene;// Obtain the scene root elementmSceneRoot = (ViewGroup) mSomeLayoutElement;// Obtain the view hierarchy to add as a child of// the scene root when this scene is enteredmViewHierarchy = (ViewGroup) someOtherLayoutElement;// Create a scenemScene = new Scene(mSceneRoot, mViewHierarchy);

5.创建场景的动作

框架能够让你自定义场景动作在系统运行时进入或者退出一个场景。在多数情况下,自定义场景动作不是必须的,由于框架动画场景之间的自动变化。

场景动作在处理以下情况是有用的:

1.将不在同一层次结构的动画视图。动画视图在开始和结束场景使用退出和进入的场景动作。

2.过渡框架中的动画视图不能自动推动的,像ListView对象。关于更多信息,请看Limitations。

为了提供自定义场景动作,定义动作为Runnable对象,可以通过 Scene.setExitAction()或者Scene.setEnterAction()。框架回调setExitAction()方法在场景开始的时候,它们要在过渡动画运行之前, setEnterAction()方法在场景结束的时候,它们要在过渡动画之后。

注意:在启动和结束场景中的视图之间不要使用场景动作传递数据。有关更多信息,请看Defining Transition Lifecycle Callbacks。

应用转移

在过渡框架中,视图层级从开始到结束的变化是用连续的帧来描述的。框架表示这些动画作为过渡对象,包含每一个动画的相关信息。运行一个动画,你提供一个过渡用过渡管理使用一个结束场景。

这节课教你在两个场景之间使用内置的转换,像移动、重置大小、淡入淡出视图。下节课将教你自定义过渡。

1.创建一个过渡

在前一节课,学习了创建一个场景来表示不同的视图的状态。一旦你定义了开始和结束场景,你想在他们之间变化的时候,你需要创建一个Transition对象来定义一个动画。框架能够让你指定一个内置转换从资源文件,或者从代码直接创建一个内置转化的实例。

这里写图片描述

2.从资源文件创建一个过渡实例

这个技巧能够让你修改定义的过渡,而不用改变activity的代码。这个技巧也是很有用的,能够从应用程序的代码中分离定义的复杂过渡,更多介绍看Specify Multiple Transitions。

从资源文件指定内置转化,有以下步骤:

1.添加 res/transition/ 目录到你的工程。

2.在这个目录下创建一个新的XML资源文件。

3.在XML中添加内置转化节点。

例如,下面就指定了一个Fade过渡。

res/transition/fade_transition.xml<fade xmlns:android="http://schemas.android.com/apk/res/android" />

下面的代码就展示了从资源文件加载一个Transition实例

Transition mFadeTransition =        TransitionInflater.from(this).        inflateTransition(R.transition.fade_transition);

3.用代码创建一个过渡实例

这个技巧是很有用的,可以动态的创建过渡对象,如果你要在代码中修改用户界面。创建一个简单的内置动画实例用很少或没有参数。

对于创建内置动画的实例,执行一个Transition子类的公共构造函数。例如,下面的代码就创建了一个Fade过渡的实例。

Transition mFadeTransition = new Fade();

4.应用一个过渡

应用一个过渡可以表示响应一个事件在不同视图之间的改变,如一个用户行为。例如,考虑一个搜索的应用程序:当用户进入一个搜索并点击搜索按钮,这个app改变场景来代表搜索结果,应用一个过渡淡出搜索按钮,淡入搜索结果。

在activity中应用一个过渡来响应某些事件,来制造场景的改变。调用TransitionManager.go()静态方法随着结束场景和用于动画的过渡实例。

TransitionManager.go(mEndingScene, mFadeTransition);

这个框架改变视图是在视图层级的视图根中,从结束场景当通过过渡实例指定运行一个动画。下一个开始场景是在结束场景过渡之后。如果没有下一个过渡,开始场景是自动决定当前的用户界面的状态。

如果你没有指定过渡实例,过渡管理能够应用自动的过渡,在恰当的情况做一些合理的事情。关于更多信息,请看TransitionManager类。

5.选择特定的目标视图

该框架默认情况下应用过渡在开始和结束场景的所有视图。在某些情况下,你可能只想应用一个动画给一个场景视图的子集。例如,该框架不支持ListView对象的改变动画,所以你不应该尝试在一个过渡中给它们添加动画。该框架能够让你选择指定的视图,给它们加你想要的动画。

每一个视图,即过渡动画都被成为Target。你只能够选择目标,把它作为视图层级的一部分和场景关联起来。

从目标列表中移除一个或者多个视图,在开始过渡之前调用removeTarget()方法。只添加一个视图到你指定的目标列表,可以调用addTarget()方法,关于更多信息,请查看Transition类。

6.指定多个过渡

从一个动画中得到更多的效果,你应该将它与场景之间发生的变化的类型相匹配。例如,如果你移除一些视图和添加其他的场景,一个淡入淡出效果的动画是显而易见的,用来指出一些视图不在可得到。如果你要移除场景中不同位置的视图,一个最好选择的动画是移动,让用户能够注意到新视图的位置。

你不必只选择一个动画,自从这个过渡框架能够让你结合动画效果在过渡集中,过渡集是包含一组独特的内置自定义过渡。

定义过渡集可以使用XML,在res/transitions/目录下创建一个资源文件,在transitionSet元素下写一些过渡列表。例如,下面的代码片段就是展示一个过渡集:

<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对象,在Activity中可以调用TransitionInflater.from()方法。TransitionSet类继承自Transition类,所以你能够使用过渡管理器来获取任何一个Transition的实例。

7.应用一个没有场景的过渡

改变视图层次结构不是修改用户界面的唯一方法。你也能够通过添加,修改,移除当前层级中的子视图作为改变。例如,你能实现一个搜索的交互在一个单独的布局中。开始的布局是一个搜索文本框和搜索图表,显示搜索结果改变用户界面,移除搜索按钮,当用户点击它的时候通过ViewGroup.removeView()方法,添加搜索结果通过ViewGroup.addView()方法。

如果另一个选择是有两个几乎相同的层次结构,你可能希望使用这种方法。宁愿创建和维护两个分离的布局文件,对于一个用户界面有微小的区别,而不愿只要一个包含视图层级的布局文件,在代码中做修改。

如果在这种方式中,在当前视图层次结构中进行更改,你不需要创建一个场景。反而,你能创建和应用一个过渡在两个不同的视图之间使用延迟过渡。当前视图在开始的状态在过渡框架中是很重要的,把视图的改变记录下来,当系统重画用户界面的时候,就可以应用过渡中动画的改变。

创建一个延迟动画在单个视图中,有以下几步:

1.当触发过渡的时间发生时,调用TransitionManager.beginDelayedTransition()方法提供所有视图的一个父视图,你就可以改变和过渡它。该框架存储了当前视图的状态和属性值。

2.根据用例所需的子视图进行更改。该框架记录了视图的改变和它们的属性值。

3.当系统根据改变重画用户界面的时候,该框架动画的改变在原始状态和新状态之间。

下面代码使用延迟过渡加载一个TextView到视图中:

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 layoutthis.setContentView(R.layout.activity_main);...// Create a new TextView and set some View propertiesmLabelText = new TextView();mLabelText.setText("Label").setId("1");// Get the root view and create a transitionmRootView = (ViewGroup) findViewById(R.id.mainLayout);mFade = new Fade(IN);// Start recording changes to the view hierarchyTransitionManager.beginDelayedTransition(mRootView, mFade);// Add the new TextView to the view hierarchymRootView.addView(mLabelText);// When the system redraws the screen to show this update,// the framework will animate the addition as a fade in

8.定义过渡的生命周期

过渡的生命周期和Activity的生命周期类似。它代表着过渡的状态,在这个期间框架的监听器通过TransitionManager.go()方法来监听,直到动画的结束。一个重要的生命周期状态,框架通过执行定义的回调实现TransitionListener接口。

过渡的生命周期是很有用的,例如,在一个场景变化的开始和结束之间复制视图的性能值。你不能简单的复制开始视图的值在结束视图层级中,因为结束视图并不代表过渡已经完成。反而,你需要存储变量的值,从结束视图中复制它,当该框架过渡完成的时候。当过渡完成的时候,发出通知,你可以在Activity的TransitionListener.onTransitionEnd()方法中实现。

关于更多信息,请看 TransitionListener类。

创建自定义转换

一个自定义过渡能够让你创建一个动画,它是不可用的从任何内置的过渡类中。例如,你能自定义一个过渡,将文本和输入字段的前景色变成灰色,表示在新屏幕中禁用的字段。这种改变能够帮助用户看见你禁用的字段。

一个自定义的过渡,像一个内置的过渡类型,在开始和结束场景的子视图中应用动画。不同于内置的过渡类型,然而,你不得不提供代码,捕获属性值和生成的动画。你可能也想定义一个目标视图的子类在你的动画中。

这节课教你捕获属性值和生成动画来创建自定义过渡。

1.扩展过渡类

创建一个自定义过渡,在你的工程中添加一个类,继承Transition类,重写其中的方法。

public class CustomTransition extends Transition {    @Override    public void captureStartValues(TransitionValues values) {}    @Override    public void captureEndValues(TransitionValues values) {}    @Override    public Animator createAnimator(ViewGroup sceneRoot,                                   TransitionValues startValues,                                   TransitionValues endValues) {}}

下面的章节解释如何重写这些方法。

2.捕获视图的属性值

过渡动画使用属性动画系统描述Property Animation。属性动画改变一个View在开始和结束之间的属性值在指定的时间内,所以这个框架需要在构造动画是的开始和结束的属性值。

然而,一个属性动画通常只需要一个视图的属性值的一小部分。例如,一个颜色动画需要颜色属性值,一个移动动画需要位置属性值。由于动画所需的属性值是特定于一个过渡,该过渡框架就不提供每一个过渡的属性值。反而,该框架执行回调方法,允许过渡只捕获它所需要的属性值,并将它们存储在框架中。

3.捕捉开始值

将开始视图值传递给框架,实现captureStartValues(transitionValues)方法。该框架回调这个方法在开始场景的每个View中。该方法的参数是一个TransitionValues对象,包含一个视图的引用,一个可以将所要查看的视图值存储在其中的Map实例。在你的实现中,恢复这些和它们通过存储在Map上的值回到框架。

为了确保属性值的key和其他的TransitionValues的key不冲突,可以使用下面的命名空间:

package_name:transition_name:property_name

下面的代码片段实现了captureStartValues()方法:

public class CustomTransition extends Transition {    // Define a key for storing a property value in    // TransitionValues.values with the syntax    // package_name:transition_class:property_name to avoid collisions    private static final String PROPNAME_BACKGROUND =            "com.example.android.customtransition:CustomTransition:background";    @Override    public void captureStartValues(TransitionValues transitionValues) {        // Call the convenience method captureValues        captureValues(transitionValues);    }    // For the view in transitionValues.view, get the values you    // want and put them in transitionValues.values    private void captureValues(TransitionValues transitionValues) {        // Get a reference to the view        View view = transitionValues.view;        // Store its background property in the values map        transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());    }    ...}

4.捕获结束值

该框架回调captureEndValues(TransitionValues)方法,在结束场景的每个目标View调用一次。在所有其他方面,captureEndValues()的工作和captureStartValues()是一样的。

下面的代码实现captureEndValues()方法:

@Overridepublic void captureEndValues(TransitionValues transitionValues) {    captureValues(transitionValues);}

在这个例子中, captureStartValues()和captureEndValues()都执行captureValues(),恢复和存储值。该视图的属性,通过captureValues()方法恢复到之前,但在开始和结束场景中有不同的值。该框架维持着一个分离的Map,里面有存储着开始和结束的视图状态。

5.创建一个自定义动画

动画的改变,在开始场景和结束场景之间状态的变化,通过重写createAnimator()方法提供一个动画。当框架回调该方法时,经过场景的根视图和TransitionValues对象,包含你捕捉开始和结束的值。

框架回调createAnimator()方法是多次的,取决于当前开始场景和结束场景之间发生的改变。例如,考虑实现一个淡入淡出的自定义动画。如果起始场景有五个目标,其中两个从结束场景中移除,结束场景有三个目标,从开始现场加一个新的目标,该框架回调createAnimator()6次,3次是目标的淡入淡出且停留在两个场景对象,在结束场景移除淡出的目标对象,回调超过2次动画方法。

目标View存在于开始场景和结束中,该框架提供了一个TransitionValues对象,有startValues和endValues参数。目标View值存在于开始场景或者结束之中,该框架提供了TransitionValues对象对于其他的相应参数和空。

实现createAnimator(ViewGroup, TransitionValues, TransitionValues)方法,当你创建一个自定义过渡,使用视图属性值捕获创建一个Animator对象,返回给框架。例如实现,请看 CustomTransition例子中的ChangeColor类。关于更多属性动画的信息,请看Property Animation。

6.应用自定义过渡

自定义过渡和内置过渡的工作流程是一样的。你能够应用自定义过渡使用一个过渡manager,具体说明请看 Applying a Transition。

0 0