第二十二章:动画(十九)

简介: 更多你自己的等待方法之前,您已经了解了如何将TaskCompletionSource与Device.StartTimer一起使用来编写自己的异步动画方法。 您还可以将TaskCompletionSource与Animation类结合使用,编写自己的异步动画方法,类似于ViewExtensions类中的方法。

更多你自己的等待方法
之前,您已经了解了如何将TaskCompletionSource与Device.StartTimer一起使用来编写自己的异步动画方法。 您还可以将TaskCompletionSource与Animation类结合使用,编写自己的异步动画方法,类似于ViewExtensions类中的方法。
假设您喜欢SlidingEntrance程序的想法,但您不满意Easing.SpringOut函数不能与TranslateTo方法一起使用。 您可以编写自己的翻译动画方法。 如果您只需要为TranslationX属性设置动画,则可以将其命名为TranslateXTo:

public static Task<bool> TranslateXTo(this VisualElement view, double x, 
                                      uint length = 250, Easing easing = null) 
{ 
    easing = easing ?? Easing.Linear; 
    askCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
    Animation animation = new Animation(
        (value) => view.TranslationX = value, // callback
        view.TranslationX, // start
        x, // end
        easing); // easing
    animation.Commit(
        view, // owner
        "TranslateXTo", // name
        16, // rate
        length, // length
        null, // easing 
        (finalValue, cancelled) => taskCompletionSource.SetResult(cancelled)); // finished
    return taskCompletionSource.Task; 
}

请注意,TranslationX属性的当前值传递给start参数的Animation构造函数,而TranslateXTo的x参数作为end参数传递。该
TaskCompletionSource的类型参数为bool,以便该方法可以指示它是否已被取消。 该方法返回TaskCompletionSource对象的Task属性,并在Commit方法的完成回调中调用SetResult。
但是,这种TranslateXTo方法存在一个微妙的缺陷。如果在动画过程中从动态树中删除了动画视觉元素,会发生什么?理论上,如果没有其他对该对象的引用,它应该有资格进行垃圾回收。但是,动画方法中会引用该对象。该元素将继续被动画化 - 并且防止被垃圾收集 - 即使没有对该元素的其他引用!
如果动画方法为动画元素创建WeakReference对象,则可以避免这种特殊情况。 WeakReference允许动画方法引用元素,但不会为了垃圾收集而增加引用计数。虽然这是您不需要为自己的应用程序中的动画方法而烦恼的事情 - 因为您可能知道何时从可视树中删除元素 - 这是您应该在库中出现的任何动画方法中执行的操作。
TranslateXTo方法位于Xamarin.FormsBook.Toolkit库中,因此它包含WeakReference的使用。因为在调用回调方法时元素可能会消失,所以该方法必须使用TryGetTarget方法获取对元素的引用。如果对象不再可用,则该方法返回false:

namespace Xamarin.FormsBook.Toolkit
{
    public static class MoreViewExtensions
    {
        public static Task<bool> TranslateXTo(this VisualElement view, double x, 
                                              uint length = 250, Easing easing = null) 
        { 
            easing = easing ?? Easing.Linear; 
            TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
            WeakReference<VisualElement> weakViewRef = new WeakReference<VisualElement>(view);
            Animation animation = new Animation(
                (value) => 
                    {
                        VisualElement viewRef;
                        if (weakViewRef.TryGetTarget(out viewRef))
                        {
                            viewRef.TranslationX = value;
                        }
                    }, // callback
                    view.TranslationX, // start
                    x, // end
                    easing); // easing
                animation.Commit(
                    view, // owner
                    "TranslateXTo", // name
                    16, // rate
                    length, // length
                    null, // easing 
                    (finalValue, cancelled) => 
                            taskCompletionSource.SetResult(cancelled)); // finished
            return taskCompletionSource.Task; 
        } 
        public static void CancelTranslateXTo(VisualElement view)
        {
            view.AbortAnimation("TranslateXTo");
        }
        __
}

请注意,还包括取消名为“TranslateX”的动画的方法。
这个TranslateXTo方法在SpringSlidingEntrance程序中进行了演示,该程序与SlidingEntrance相同,只是它引用了Xamarin.FormsBook.Toolkit库和OnAppearing覆盖调用TranslateXTo:

public partial class SpringSlidingEntrancePage : ContentPage
{
    public SpringSlidingEntrancePage()
    {
        InitializeComponent();
    }
    async protected override void OnAppearing()
    {
        base.OnAppearing();
        double offset = 1000;
        foreach (View view in stackLayout.Children)
        {
            view.TranslationX = offset;
            offset *= -1;
        }
        foreach (View view in stackLayout.Children)
        {
            await Task.WhenAny(view.TranslateXTo(0, 1000, Easing.SpringOut),
                                Task.Delay(100));
        }
    }
}

不同的是,我相信你会同意,非常值得付出努力。 在进入有序页面之前,页面上的元素会滑入并超出目的地。
Xamarin.FormsBook.Toolkit库还有一个TranslateYTo方法,它与TranslateXTo基本相同,但语法更简洁:

namespace Xamarin.FormsBook.Toolkit
{
    public static class MoreViewExtensions
    {
        __
        public static Task<bool> TranslateYTo(this VisualElement view, double y,
                                         uint length = 250, Easing easing = null)
        {
            easing = easing ?? Easing.Linear;
            TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
            WeakReference<VisualElement> weakViewRef = new WeakReference<VisualElement>(view);
            Animation animation = new Animation((value) =>
                {
                    VisualElement viewRef;
                    if (weakViewRef.TryGetTarget(out viewRef))
                    {
                        viewRef.TranslationY = value;
                    }
                }, view.TranslationY, y, easing);
            animation.Commit(view, "TranslateYTo", 16, length, null,
                             (v, c) => taskCompletionSource.SetResult(c));
            return taskCompletionSource.Task;
        }
        public static void CancelTranslateYTo(VisualElement view)
        {
            view.AbortAnimation("TranslateYTo");
        }
        __
}

作为TranslateTo的替代品,您可以使用TranslateXYTo。 正如您在本章前面所了解的那样,返回小于0或大于1的值的Easing函数不应传递给带有子项的动画的Commit方法。 相反,应该将Easing函数传递给子元素的Animation构造函数。 这就是TranslateXYTo的作用:

namespace Xamarin.FormsBook.Toolkit
{
    public static class MoreViewExtensions
    {
        __
        public static Task<bool> TranslateXYTo(this VisualElement view, double x, double y,
                                         uint length = 250, Easing easing = null)
        {
            easing = easing ?? Easing.Linear;
            TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
            WeakReference<VisualElement> weakViewRef = new WeakReference<VisualElement>(view);
            Action<double> callbackX = value =>
                {
                    VisualElement viewRef;
                    if (weakViewRef.TryGetTarget(out viewRef))
                    {
                        viewRef.TranslationX = value;
                    }
                };
            Action<double> callbackY = value =>
                {
                    VisualElement viewRef;
                    if (weakViewRef.TryGetTarget(out viewRef))
                    {
                        viewRef.TranslationY = value;
                    }
                };
            Animation animation = new Animation
            {
                { 0, 1, new Animation(callbackX, view.TranslationX, x, easing) },
                { 0, 1, new Animation(callbackY, view.TranslationY, y, easing) }
            };
            animation.Commit(view, "TranslateXYTo", 16, length, null,
                                 (v, c) => taskCompletionSource.SetResult(c));
            return taskCompletionSource.Task;
        }
        public static void CancelTranslateXYTo(VisualElement view)
        {
            view.AbortAnimation("TranslateXYTo");
        }
        __
    }
}
目录
相关文章
|
JavaScript Android开发
第二十二章:动画(二十一)
使用AnimationExtensions为什么ViewExtensions不包含ColorTo动画? 这种方法没有你最初假设的那么明显有三个可能的原因:首先,VisualElement定义的唯一Color属性是BackgroundColor,但通常不是要设置动画的Color属性。
545 0
|
JavaScript Android开发
第二十二章:动画(二十)
实现贝塞尔动画一些图形系统实现动画,该动画沿着贝塞尔曲线移动视觉对象,甚至(可选地)旋转视觉对象,使其保持与曲线相切。Bezier曲线以法国工程师兼数学家PierreBézier的名字命名,他在雷诺工作期间开发了用于汽车车身交互式计算机辅助设计的曲线。
617 0
|
Android开发
第二十二章:动画(十八)
超越高级动画方法你到目前为止看到的ConcurrentAnimations中的例子仅限于Scale和Rotate属性的动画,因此它们没有显示任何你无法做的事情。ViewExtensions类中的方法。
702 0
|
Android开发
第二十二章:动画(十七)
子动画ConcurrentAnimations中的前两个示例是单个动画。 Animation类还支持子动画,这就是标记为“Animation 3”的Button的处理程序。 它首先使用无参数构造函数创建父动画对象。
681 0
|
JavaScript Android开发
第二十二章:动画(十五)
深入动画 在第一次遇到时,完整的Xamarin.Forms动画系统可能会有点混乱。 让我们从可用于定义动画的三个公共类的全局视图开始。整理课程除了Easing类之外,Xamarin.Forms动画系统还包含三个公共类。
823 0
|
JavaScript Android开发
第二十二章:动画(十六)
使用Animation类让我们对Animation类进行一些实验。 这涉及实例化Animation类型的对象,然后调用Commit,它实际上开始动画。 Commit方法不返回Task对象; 相反,Animation类完全通过回调提供通知。
705 0
|
JavaScript Android开发
第二十二章:动画(十四)
你自己的等待动画在本章的下一节中,您将看到Xamarin.Forms实现的基础动画基础结构。这些底层方法允许您定义自己的动画函数,这些函数返回Task对象,并且可以与await一起使用。在第20章“异步和文件I / O”中,您了解了如何使用静态Task.Run方法创建执行的辅助线程,以执行像Mandelbrot计算这样的密集后台作业。
711 0
|
JavaScript Android开发 iOS开发
第二十二章:动画(十二)
永远的动画在入口动画的相反极端是永远的动画。 应用程序可以实现“永远”或至少在程序结束之前进行的动画。 这种动画的唯一目的通常是展示动画系统的功能,但最好是以令人愉快或有趣的方式。第一个示例称为FadingTextAnimation,并使用FadeTo淡入和淡出两个Label元素。
642 0
|
JavaScript Android开发
第二十二章:动画(十一)
入口动画实际编程中的一种常见类型的动画是在页面首次可见时发生的。 页面上的各种元素可以在进入最终状态之前进行简要动画处理。 这通常被称为入口动画,可能涉及: 翻译,将元素移动到最终位置。 缩放,将元素放大或缩小到最终尺寸。
843 0
|
JavaScript Android开发 iOS开发
第二十二章:动画(十三)
动画Bounds属性也许ViewExtensions类中最好奇的扩展方法是LayoutTo。参数是一个Rectangle值,第一个问题可能是:此方法的动画属性是什么? VisualElement定义的Rectangle类型的唯一属性是Bounds属性。
677 0