等待动画
解决后续点击问题的另一种方法是在调用RotateTo之前初始化Rotation属性:
void OnButtonClicked(object sender, EventArgs args)
{
button.Rotation = 0;
button.RotateTo(360, 2000);
}
现在,您可以在停止后再次点击按钮,它将从头开始动画。 按钮旋转时重复点击也表现不同:它们从0度开始。
有趣的是,代码中的这种轻微变化不允许后续的点击:
void OnButtonClicked(object sender, EventArgs args)
{
button.RotateTo(360, 2000);
button.Rotation = 0;
}
此版本的行为与仅具有RotateTo方法的版本相同。 看起来似乎在该调用之后将Rotation属性设置为0。
为什么不起作用? 它不起作用,因为RotateTo方法是异步的。 启动动画后,该方法会快速返回,但动画本身会在后台发生。 在RotateTo方法返回时将Rotation属性设置为0没有明显的效果,因为
该设置很快被背景RotateTo动画取代。
因为该方法是异步的,所以RotateTo返回一个Task对象 - 更具体地说,返回一个Task 对象 - 这意味着您可以调用ContinueWith来指定动画终止时调用的回调函数。 然后,回调可以在动画完成后将Rotation属性设置为0:
void OnButtonClicked(object sender, EventArgs args)
{
button.RotateTo(360, 2000).ContinueWith((task) =>
{
button.Rotation = 0;
});
}
传递给ContinueWith的任务对象的类型为Task ,ContinueWith回调可以使用Result属性来获取该布尔值。 如果动画被取消,则值为true;如果动画完成,则该值为false。 您可以通过使用Debug.WriteLine调用显示返回值并在Visual Studio或Xamarin Studio的“输出”窗口中查看结果来轻松确认:
void OnButtonClicked(object sender, EventArgs args)
{
button.RotateTo(360, 2000).ContinueWith((task) =>
{
System.Diagnostics.Debug.WriteLine("Cancelled? " + task.Result);
button.Rotation = 0;
});
}
如果在动画时点按按钮,您将看到返回的真值。 每次调用RotateTo都会取消之前的动画。 如果让动画运行完成,您将看到返回错误值。
使用RotateTo方法比使用ContinueWith更可能使用await:
async void OnButtonClicked(object sender, EventArgs args)
{
bool wasCancelled = await button.RotateTo(360, 2000);
button.Rotation = 0;
}
或者,如果您不关心返回值:
async void OnButtonClicked(object sender, EventArgs args)
{
await button.RotateTo(360, 2000);
button.Rotation = 0;
}
请注意处理程序上的async修饰符,这是包含await运算符的任何方法所必需的。
如果您使用过其他动画系统,如果您希望在动画完成时通知应用程序,则很可能需要定义回调方法。等待,确定动画何时完成 - 或许执行其他一些代码 - 变得微不足道。在这个特定的例子中,执行的代码非常简单,但当然它可能更复杂。
有时你会想让你的动画在后台运行完成 - 在这种情况下,没有必要使用等待它们 - 有时你会想要在动画完成时做一些事情。但请注意:如果Button也触发了一些实际的应用程序功能,您可能不想等到动画结束后再执行该操作。
RotateTo和RelRotateTo是ViewExtensions类中定义的几个类似方法中的两个。您将在本章中看到的其他内容包括ScaleTo,TranslateTo,FadeTo和LayoutTo。如果动画在没有中断的情况下完成,它们都返回Task objects-false,如果取消则返回true。
您的应用程序可以通过调用静态方法ViewExtensions.CancelAnimations取消这些动画中的一个或多个。与ViewExtensions中的所有其他方法不同,这不是扩展方法。你需要像这样调用它:
ViewExtensions.CancelAnimations(button)
这将立即取消由ViewExtensions类中当前在按钮对象上运行的扩展方法启动的所有动画。
使用await对于堆叠顺序动画特别有用:
async void OnButtonClicked(object sender, EventArgs args)
{
await button.RotateTo(90, 250);
await button.RotateTo(-90, 500);
await button.RotateTo(0, 250);
}
此处定义的总动画需要一秒钟。按钮在第一个四分之一秒顺时针旋转90度,然后在下半秒逆时针旋转180度,然后顺时针旋转90度再次以0度结束。您需要等待前两个,以便它们是顺序的,但如果在第三个动画完成后没有其他任何东西可以在Clicked处理程序中执行,则您不需要它在第三个上。
像这样的复合动画通常被称为关键帧动画。您正在指定一系列旋转角度和时间,整个动画在这些之间进行插值。在大多数动画系统中,关键帧动画通常比简单动画更难使用。但随着等待,关键帧动画变得微不足道。
Task 的返回值不一定表示动画正在辅助线程中运行。实际上,动画的至少一部分 - 实际设置Rotation属性的部分 - 必须在用户界面线程中运行。从理论上讲,整个动画可以在用户界面线程中运行。正如您在上一章中看到的那样,使用Device.StartTimer或Task.Delay创建的动画完全在用户界面线程中运行,尽管底层计时器机制可能涉及辅助线程。
您将在本章后面看到动画方法如何仍然可以返回Task对象,但完全在用户界面线程中运行。此技术允许代码使用计时器来调整动画,但在代码完成时仍然提供基于任务的结构化通知。