深入动画
在第一次遇到时,完整的Xamarin.Forms动画系统可能会有点混乱。 让我们从可用于定义动画的三个公共类的全局视图开始。
整理课程
除了Easing类之外,Xamarin.Forms动画系统还包含三个公共类。 在这里,它们按层次顺序从高级别到低级别:
ViewExtensions类
这是你已经看过的课程。 ViewExtensions是一个静态类,它包含VisualElement的几个扩展方法,它是View和Page的父类:
- TranslateTo为TranslationX和TranslationY属性设置动画
- ScaleTo为Scale属性设置动画
- ResScaleTo将动画增量增加或减少应用于Scale属性
- RotateTo为“旋转”属性设置动画
- RelRotateTo将动画增量增加或减少应用于“旋转”属性
- RotateXTo为RotationX属性设置动画
- RotateYTo为RotationY属性设置动画
- FadeTo为Opacity属性设置动画
- Layout通过调用Layout方法为get-only Bounds属性设置动画
如您所见,前七种方法以转换属性为目标。 这些属性不会导致在布局中如何感知元素的任何更改。 虽然动画视图可以移动,更改大小和旋转,但页面上的其他任何视图都不会受到影响,除非可能被新位置或大小遮挡。
FadeTo动画仅更改Opacity属性,因此也不会导致布局更改。
正如您所见,LayoutTo动画有点不同。 参数是一个Rectangle值,该方法实质上覆盖了元素的父Layout或Layout 对象分配给视图的位置和大小。 LayoutTo对于为AbsoluteLayout的子项设置动画非常有用,因为您可以使用相同的Rectangle对象调用AbsoluteLayout.SetLayoutBounds
动画完成后。 在第26章中,您将学习如何在从Layout 派生的类中使用LayoutTo。
这些都是返回Task 的异步方法。 如果动画被取消,则布尔返回值为true;如果动画完成,则返回false。
此外,ViewExtensions还包含一个静态ViewExtensions.CancelAnimations方法(不是扩展方法),该方法具有VisualElement类型的单个参数。 此方法取消在该VisualElement对象上使用此类启动的所有动画。
ViewExtensions中的所有扩展方法都可以通过创建一个或多个Animation对象然后调用该Animation类定义的Commit方法来工作。
动画类
Animation类有两个构造函数:一个无参数构造函数和另一个带有五个参数的构造函数,尽管只需要一个参数:
public Animation (Action<double> callback,
double start = 0.0f,
double end = 1.0f,
Easing easing = null,
Action finished = null)
这定义了一个double值的动画,它从start开始到结束。 通常,这两个参数的默认值分别为0和1。 动画值作为参数传递给回调方法,通常将其命名为t或progress。 回调可以使用此值执行任何操作,但通常用于更改属性的值。 如果target属性的类型为double,则start和end值可以直接定义动画属性的开始值和结束值。
动画实现IEnumerable接口。 它可以维护一组子动画,然后可以统一启动并保持同步。 要允许程序将项添加到此集合,Animation定义了四种方法:
- Add
- Insert
- WithConcurrent (两个版本)
这些都基本相同,因为它们都将子动画对象添加到由Animation维护的内部集合中。 你很快就会看到例子。
启动动画(可能包括或不包括子动画)需要调用Commit方法。 Commit方法指定动画的持续时间,还包括两个回调:
animation.Commit(IAnimatable owner,
string name,
uint rate = 16,
uint length = 250,
Easing easing = null,
Action<double, bool> finished = null,
Func<bool> repeat = null);
注意第一个参数是IAnimatable。 IAnimatable接口只定义了两个方法,名为BatchBegin和BatchCommit。 实现IAnimatable的唯一类是VisualElement,它是与ViewExtensions方法关联的类。
name参数标识动画。 您可以使用AnimationExtensions类(即将出现)中的方法来确定该名称的动画是否正在运行或取消它。 您不需要为正在运行的每个动画使用唯一的名称,但如果您在同一个可视对象上进行多个重叠的Commit调用,那么这些名称应该是唯一的。
理论上,rate参数表示每次调用Animation构造函数中定义的回调方法之间的毫秒数。 对于每秒60帧的动画速度,它设置为16,但更改它没有任何效果。
重复回调允许重复动画。 它在动画结束时调用,如果回调返回true,则表示应该重复动画。 正如您将看到的,它适用于某些配置但不适用于其他配置。
Animation类中的Commit方法通过调用AnimationExtensions类中的Animate方法来工作。
AnimationExtensions类
与ViewExtensions一样,AnimationExtentions是一个静态类,主要包含扩展方法。 但是,虽然ViewExtensions方法中的第一个参数是VisualElement,但AnimationExtensions方法中的第一个参数是IAnimatable,以与Animation类中的Commit方法保持一致。
AnimationExtensions使用回调和其他信息定义Animate方法的几个重载。 最广泛的Animate版本是这种通用方法:
public static void Animate<T>(this IAnimatable self,
string name,
Func<double, T> transform,
Action<T> callback,
uint rate = 16,
uint length = 250,
Easing easing = null,
Action<T, bool> finished = null,
Func<bool> repeat = null);
从某种意义上说,这是您需要的唯一动画方法。 到目前为止,许多这些参数应该是可识别的。 但请注意可以帮助构建动画逻辑的转换方法
目标属性不是double类型。
例如,假设您要为Color类型的属性设置动画。 首先编写一个小变换方法,接受从0到1(通常命名为t或progress)的double参数,并返回与该值对应的Color值。 回调方法获取Color值,然后可以将其设置为特定对象的特定属性。 您将在本章末尾看到这个精确的应用程序。
AnimationExtensions类中的其他公共方法是AnimationIsRunning,用于确定特定VisualElement实例上的特定动画是否正在运行,以及AbortAnimation是否取消动画。 两者都是IAnimatable的扩展方法,并且要求名称与传递给Animate方法的名称或Animation的Commit方法一致。