【Animations】使用转场动画布局更改(10)

原文

概要


使用转场动画布局更改

Android的转换框架允许您通过简单提供开始布局和结束布局来为UI中的各种动作制作动画。您可以选择您想要的动画类型(例如淡入淡出视图或更改视图大小),并且过渡框架将指出如何从开始布局到结束布局进行动画处理。

转换框架包含以下功能:

  • 组级动画:将一个或多个动画效果应用于视图层次结构中的所有视图。
  • 内置动画:使用预定义的动画来实现淡入淡出或移动等常见效果。
  • 资源文件支持:从布局资源文件加载视图层次结构和内置动画。
  • 生命周期回调:接收提供对动画和层次结构更改过程的控制的回调。

注意:此页面描述如何在同一活动中的布局之间构建转换。如果用户在活动之间移动,则应该阅读使用动画开始活动。

有关布局更改之间动画的示例代码,请参阅 BasicTransition
两个布局之间动画的基本过程如下所示:

  1. Scene为起始布局和结束布局创建一个对象。但是,起始布局的场景通常是从当前布局自动确定的。
  2. 创建一个Transition对象来定义你想要的动画类型。
  3. 调用TransitionManager.go()并且系统运行动画来交换布局。

图1中的图表说明了布局,场景,过渡和最终动画之间的关系。
【Animations】使用转场动画布局更改(10)
图1.转换框架如何创建动画的基本说明

创建一个场景


场景存储视图层次结构的状态,包括其所有视图及其属性值。转换框架可以在开始和结束场景之间运行动画。

您可以从布局资源文件或代码中的一组视图创建场景。但是,转换的起始场景通常是从当前UI自动确定的。

场景还可以定义自己在进行场景更改时运行的操作。例如,此功能对于在转换到场景后清理视图设置很有用。

注意:框架可以在不使用场景的情况下对单个视图层次结构中的更改进行动画制作,如 应用无场景转场中所述。但是,理解场景对于处理转换至关重要。

从布局资源创建场景

您可以Scene直接从布局资源文件创建实例。当文件中的视图层次结构大部分是静态的时使用这种技术。生成的场景表示创建Scene实例时的视图层次结构的状态 。如果您更改视图层次结构,则必须重新创建场景。该框架根据文件中的整个视图层次结构创建场景; 您不能从布局文件的一部分创建场景。

要从Scene布局资源文件创建实例,请从布局中检索场景根作为ViewGroup实例,然后Scene.getSceneForLayout()使用包含场景的视图层次结构的布局文件的场景根和资源ID 调用该 方法。

定义场景的布局

本节其余部分的代码片段向您展示了如何使用相同的场景根元素创建两个不同的场景。片段还表明,您可以加载多个不相关的Scene对象,而不会暗示它们彼此相关。

该示例由以下布局定义组成:

具有文本标签和子布局的活动的主布局。
具有两个文本字段的第一个场景的相对布局。
第二个场景的相对布局,具有相同的两个文本字段,顺序不同。
该示例的设计使所有动画都在活动主布局的子布局内发生。主布局中的文本标签保持静态。

活动的主要布局定义如下:

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>

此布局定义包含场景根目录的文本字段和子布局。第一个场景的布局包含在主布局文件中。这允许应用程序将其显示为初始用户界面的一部分,并将其加载到场景中,因为框架只能将整个布局文件加载到场景中。

第一个场景的布局定义如下:
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>

第二个场景的布局包含相同的两个文本字段(具有相同的ID),其排列顺序不同,定义如下:
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>

从布局生成场景

在为两个相对布局创建定义后,您可以为每个布局获取一个场景。这使您可以稍后在两种UI配置之间切换。要获取场景,需要引用场景根和布局资源ID。

以下代码片段显示如何获取对场景根目录的引用并Scene从布局文件创建两个对象:

Scene mAScene;
Scene mAnotherScene;

// Create the scene root for the scenes in this app
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes
mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);
mAnotherScene =
    Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);

在应用程序中,现在有两个Scene基于视图层次结构的对象。两个场景都使用由FrameLayout元素定义的场景根 res/layout/activity_main.xml。

在您的代码中创建一个场景

你也可以Scene在你的代码中从一个ViewGroup对象中创建一个实例 。在代码中直接修改视图层次结构或动态生成视图层次结构时,请使用此技术。

要从代码中的视图层次结构创建场景,请使用 Scene(sceneRoot, viewHierarchy) 构造函数。调用这个构造函数等价于Scene.getSceneForLayout()当你已经膨胀一个布局文件时调用该 方法。

以下代码片段演示了如何根据代码中Scene 的场景根元素和场景的视图层次结构创建实例:

Scene mScene;

// Obtain the scene root element
mSceneRoot = (ViewGroup) mSomeLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered
mViewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene
mScene = new Scene(mSceneRoot, mViewHierarchy);

创建场景动作

该框架使您能够定义在进入或退出场景时系统运行的自定义场景操作。在很多情况下,定义自定义场景动作不是必需的,因为框架会自动动画化场景之间的变化。

场景操作对于处理这些情况很有用:

  • 使不在同一层次结构中的视图动画化。您可以使用退出和录入场景操作为开始和结束场景设置动画视图。
  • 为过渡框架无法自动生成动画的动画设置动画,如 ListView对象。有关更多信息,请参阅 限制。

要提供自定义场景动作,请将您的动作定义为Runnable对象并将其传递给Scene.setExitAction()或 Scene.setEnterAction()方法。setExitAction()在运行过渡动画之后,框架在运行过渡动画和setEnterAction()结束场景上的方法之前调用开始场景上的方法。

注意:不要使用场景动作在起始场景和结束场景中的视图之间传递数据。有关更多信息,请参阅 定义转换生命周期回调。

应用转场


过渡框架表示场景与Transition对象之间的动画风格 。你可以Transition使用几个内置的子类来实例化 ,比如AutoTransition和 Fade,或者定义你自己的转换。然后,可以通过传递结尾Scene 和结束Transition来 在场景之间运行动画TransitionManager.go()。

过渡生命周期与活动生命周期相似,它表示框架监视动画开始和完成之间的过渡状态。在重要的生命周期状态中,框架会调用可以实现的回调方法,以便在转换的不同阶段对用户界面进行调整。

创建一个转换

在上一节中,您学习了如何创建表示不同视图层次结构状态的场景。一旦你定义了你想要改变的开始场景和结束场景,你需要创建一个Transition定义动画的对象。该框架使您能够在资源文件中指定内置转换并在代码中对其进行膨胀,或直接在代码中创建内置转换的实例。

表1.内置转换类型。
【Animations】使用转场动画布局更改(10)

从资源文件创建转换实例

该技术使您能够修改您的转换定义,而无需更改您的活动代码。此技术对于将复杂转换定义与应用程序代码分开也很有用,如指定多个转换中所示。

要在资源文件中指定内置转换,请按照下列步骤操作:

  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);

在您的代码中创建一个转换实例

如果您修改代码中的用户界面,并且创建具有很少参数或无参数的简单内置转换实例,则此技术对于动态创建转换对象非常有用。

要创建内置转换的实例,请调用Transition该类的子类中的一个公共构造函数。例如,下面的代码片段创建了一个Fade转换的实例:

Transition mFadeTransition = new Fade();

应用转场

您通常应用一个转换来响应不同视图层次结构之间的更改,以响应事件(如用户操作)。例如,考虑一个搜索应用程序:当用户输入搜索词并单击搜索按钮时,应用程序将更改为代表结果布局的场景,同时应用淡出搜索按钮并淡入搜索结果的转换。

要在响应活动中的某个事件应用转场时进行场景更改,请调用TransitionManager.go() 具有结束场景和转场实例的静态方法以用于动画,如以下代码片段所示:

TransitionManager.go(mEndingScene, mFadeTransition);

当运行由转换实例指定的动画时,框架使用结束场景中的视图层次结构更改场景根中的视图层次结构。起始场景是上一次转场的结局场景。如果没有先前的转换,则从用户界面的当前状态自动确定起始场景。

如果您未指定转换实例,则转换管理器可以应用自动转换,以便在大多数情况下执行合理的操作。有关更多信息,请参阅TransitionManager该类的API参考。

选择特定的目标视图

默认情况下,该框架将开始和结束场景中的所有视图应用转换。在某些情况下,您可能只想将动画应用于场景中的一部分视图。例如,该框架不支持对ListView对象进行动画更改 ,因此您不应该在转换过程中尝试对它们进行动画处理。该框架使您能够选择想要制作动画的特定视图。

过渡动画的每个视图被称为目标。您只能选择属于与场景关联的视图层次结构的一部分的目标。

要从目标列表中删除一个或多个视图,请removeTarget()在开始转换之前调用该 方法。要仅将您指定的视图添加到目标列表,请调用该 addTarget()方法。有关更多信息,请参阅Transition该类的API参考。

指定多个转换

为了从动画中获得最大的影响力,您应该将其与场景之间发生的更改类型进行匹配。例如,如果要删除一些视图并在场景之间添加其他视图,则动画中的淡出/淡入提供了一些显而易见的指示,表明某些视图不再可用。如果您将视图移动到屏幕上的不同位置,则更好的选择是使移动动画,以便用户注意视图的新位置。

您不必只选择一个动画,因为转换框架使您能够将动画效果合并到包含一组单独的内置或自定义转场的转场集中。

要从XML转换集合中定义转换集,请在该res/transitions/目录中创建一个资源文件并列出该transitionSet元素下的转换 。例如,以下片段显示了如何指定与AutoTransition 该类具有相同行为的转换集:

<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代码中的转换集合膨胀为对象,请TransitionInflater.from() 在您的活动中调用该方法。这个TransitionSet类从 Transition类中继承,所以你可以像转换任何其他Transition实例一样使用转换管理器。

应用没有场景的转场


更改视图层次结构不是修改用户界面的唯一方法。您还可以通过添加,修改和删除当前层次结构中的子视图来进行更改。例如,您可以使用单一布局实现搜索交互。从显示搜索条目字段和搜索图标的版面开始。要更改用户界面以显示结果,请在用户通过调用ViewGroup.removeView()方法单击该按钮时删除搜索按钮,并通过调用ViewGroup.addView()方法添加搜索结果。

如果替代方案具有两个几乎相同的层次结构,则可能需要使用此方法。您不必为用户界面中的细微差异创建和维护两个单独的布局文件,您可以让一个布局文件包含您在代码中修改的视图层次结构。

如果您以这种方式在当前视图层次结构中进行更改,则不需要创建场景。相反,您可以使用延迟转换在视图层次结构的两个状态之间创建和应用转换。转换框架的这一特性从当前视图层次结构状态开始,记录您对视图所做的更改,并在系统重新绘制用户界面时应用一个转换动画更改。

要在单个视图层次结构中创建延迟的转换,请按照下列步骤操作:

  1. 当触发转换的事件发生时,调用TransitionManager.beginDelayedTransition()提供您想要更改的所有视图和要使用的转换的父视图的方法。该框架存储子视图的当前状态及其属性值。
  2. 根据您的用例要求更改子视图。该框架记录您对子视图及其属性所做的更改。
  3. 当系统根据您的更改重新绘制用户界面时,框架会动态改变原始状态和新状态之间的更改。

以下示例说明如何使用延迟转换将文本视图添加到视图层次结构中。第一个片段显示布局定义文件:
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
this.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

定义转换生命周期回调

转换生命周期与活动生命周期相似。它表示框架在调用TransitionManager.go()方法和完成动画之间的时间内监视的过渡状态 。在重要的生命周期状态中,框架调用由TransitionListener 接口定义的回调。

例如,过渡生命周期回调很有用,用于在场景更改期间将视图属性值从开始视图层次结构复制到结束视图层次结构。您不能简单地将值从其起始视图复制到结束视图层次结构中的视图,因为直到转换完成时,结束视图层次结构才会膨胀。相反,您需要将值存储在变量中,然后在框架完成转换时将其复制到结束视图层次结构中。要在转换完成时收到通知,您可以TransitionListener.onTransitionEnd()在您的活动中实施该方法。

有关更多信息,请参阅TransitionListener该类的API参考。

限制


本节列出了转换框架的一些已知限制:

  • 应用于a的动画SurfaceView可能无法正确显示。 SurfaceView实例从非UI线程更新,因此更新可能与其他视图的动画不同步。
  • 某些特定的过渡类型应用于a时可能不会产生所需的动画效果TextureView。
  • 扩展的类AdapterView,例如 ListView,以与转换框架不兼容的方式管理其子视图。如果您尝试根据某个视图设置动画 AdapterView,设备显示可能会挂起。
  • 如果尝试TextView使用动画调整大小,文本将在对象完全调整大小之前弹出到新的位置。为避免此问题,请不要为包含文本的视图调整大小。

    Lastest Update:2018.04.25

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

【Animations】使用转场动画布局更改(10)

猜你喜欢

转载自blog.51cto.com/4789781/2120986