天天看點

第二十二章:動畫(五)

等待動畫

解決後續點選問題的另一種方法是在調用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對象,但完全在使用者界面線程中運作。此技術允許代碼使用計時器來調整動畫,但在代碼完成時仍然提供基于任務的結構化通知。

繼續閱讀