更多事件觸發器
前一章關于動畫的章節展示了一個按鈕,它在點選時旋轉或縮放。 雖然大多數動畫示例都是為了制作有趣的示範而采取極端措施,但是按鈕用一點動畫來響應點選并不是不合理的。 這是EventTrigger的完美工作。
這是另一個TriggerAction派生物。 它與ScaleAction類似,但包括對ScaleTo的兩次調用,而不是一次,是以命名為ScaleUpAndDownAction:
namespace Xamarin.FormsBook.Toolkit
{
public class ScaleUpAndDownAction : TriggerAction<VisualElement>
{
public ScaleUpAndDownAction()
{
Anchor = new Point(0.5, 0.5);
Scale = 2;
Length = 500;
}
public Point Anchor { set; get; }
public double Scale { set; get; }
public int Length { set; get; }
protected override async void Invoke(VisualElement visual)
{
visual.AnchorX = Anchor.X;
visual.AnchorY = Anchor.Y;
await visual.ScaleTo(Scale, (uint)Length / 2, Easing.SinOut);
await visual.ScaleTo(1, (uint)Length / 2, Easing.SinIn);
}
}
}
該類對Easing函數進行寫死以保持代碼簡單。
ButtonGrowth程式定義了一個内部Style,它設定了三個Button屬性,并包含一個EventTrigger,它使用預設參數調用ScaleUpAndDownAction以響應Clicked事件:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="ButtonGrowth.ButtonGrowthPage">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="FontSize" Value="Large" />
<Style.Triggers>
<EventTrigger Event="Clicked">
<toolkit:ScaleUpAndDownAction />
</EventTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text="Button #1" />
<Button Text="Button #2" />
<Button Text="Button #3" />
</StackLayout>
</ContentPage>
這是三個按鈕,因為它們的大小随着點選而增長:
是否有可能在這裡使用兩個ScaleAction執行個體而不是ScaleUpAndDownAction-一個執行個體将Button按比例縮放,另一個執行個體将其縮小? 不。我們隻處理一個事件 - Clicked事件 - 當事件被觸發時必須調用所有事件。 EventTrigger當然可以調用多個動作,但這些動作同時發生。 同時運作的兩個ScaleAction執行個體将互相競争。
但是,有一個解決方案。 這是一個DelayedScaleAction類,它派生自ScaleAction,但在ScaleTo調用之前包含一個Task.Delay調用:
namespace Xamarin.FormsBook.Toolkit
{
public class DelayedScaleAction : ScaleAction
{
public DelayedScaleAction() : base()
{
// Set defaults.
Delay = 0;
}
public int Delay { set; get; }
async protected override void Invoke(VisualElement visual)
{
visual.AnchorX = Anchor.X;
visual.AnchorY = Anchor.Y;
await Task.Delay(Delay);
await visual.ScaleTo(Scale, (uint)Length, Easing);
}
}
}
您現在可以修改ButtonGrowth XAML檔案以包含由Clicked事件觸發的兩個DelayedScaleAction對象。 這些都是同時調用的,但第二個的Delay屬性設定為與第一個的Length屬性相同的值,是以第一個ScaleTo在第二個ScaleTo開始時結束:
<Style TargetType="Button">
__
<Style.Triggers>
<EventTrigger Event="Clicked">
<toolkit:DelayedScaleAction Scale="2"
Length="250"
Easing="SinOut" />
<toolkit:DelayedScaleAction Delay="250"
Scale="1"
Length="250"
Easing="SinIn" />
</EventTrigger>
</Style.Triggers>
</Style>
DelayedScaleAction比ScaleUpAndDownAction更難使用,但它更靈活,您還可以定義名為DelayedTranslateAction和DelayedRotateAction的類來添加到混合中。
在上一章中,您看到了一個名為JiggleButton的Button衍生物,它在單擊Button時運作一個簡短的動畫。 這是一種動畫,您可以使用TriggerAction實作。 優點是您可以将它與普通的Button類一起使用,并可能将效果與特定類型的視圖和特定事件分開,以便可以使用它
與其他觀點和其他事件。
這是一個TriggerAction派生,它實作了與JiggleButton相同類型的動畫,但有三個屬性使其更靈活。 為了更清楚地将它與早期的代碼區分開來,這個類的名稱是ShiverAction:
namespace Xamarin.FormsBook.Toolkit
{
public class ShiverAction : TriggerAction<VisualElement>
{
public ShiverAction()
{
Length = 1000;
Angle = 15;
Vibrations = 10;
}
public int Length { set; get; }
public double Angle { set; get; }
public int Vibrations { set; get; }
protected override void Invoke(VisualElement visual)
{
visual.Rotation = 0;
visual.AnchorX = 0.5;
visual.AnchorY = 0.5;
visual.RotateTo(Angle, (uint)Length,
new Easing(t => Math.Sin(Math.PI * t) *
Math.Sin(Math.PI * 2 * Vibrations * t)));
}
}
}
請注意,Invoke将目标可視元素的Rotation屬性初始化為零。 這是為了避免連續兩次按下按鈕時出現問題,并且在前一個動畫仍在運作時調用Invoke。
ShiverButtonDemo程式的XAML檔案定義了一個隐式Style,其中包含ShiverAction,其極值設定為三個屬性:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="ShiverButtonDemo.ShiverButtonDemoPage">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="FontSize" Value="Large" />
<Style.Triggers>
<EventTrigger Event="Clicked">
<toolkit:ShiverAction Length="3000"
Angle="45"
Vibrations="25" />
</EventTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text="Button #1" />
<Button Text="Button #2" />
<Button Text="Button #3" />
</StackLayout>
</ContentPage>
三個Button元素共享同一個ShiverAction執行個體,但每次調用Invoke方法都是針對特定的Button對象。每個按鈕發抖都獨立于其他按鈕。
但是,如果您想使用ShiverAction來響應元素上的Tapped事件而不是按鈕上的Clicked事件,例如,使某些内容或圖像振動幀,該怎麼辦? Tapped事件僅由TapGestureRecognizer定義,但您無法将EventTrigger附加到TapGestureRecognizer,因為TapGestureRecognizer沒有Triggers集合。您也不能将EventTrigger附加到View對象并指定Tapped事件。在View對象上找不到Tapped事件。
解決方案是使用行為,本章後面将對此進行說明。
也可以使用EventTrigger對象進行條目驗證。這是一個名為NumericValidationAction的TriggerAction派生,其泛型參數為Entry,是以它僅适用于Entry視圖。調用Invoke時,參數是Entry對象,是以它可以通路特定于Entry的屬性,在本例中為Text和TextColor。該方法檢查Entry的Text屬性是否可以解析為有效的double。如果沒有,文本将顯示為紅色以提醒使用者:
namespace Xamarin.FormsBook.Toolkit
{
public class NumericValidationAction : TriggerAction<Entry>
{
protected override void Invoke(Entry entry)
{
double result;
bool isValid = Double.TryParse(entry.Text, out result);
entry.TextColor = isValid ? Color.Default : Color.Red;
}
}
}
您可以将此代碼附加到帶有TextThanged事件的EventTrigger的條目,如TriggerEntryValidation程式中所示:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="TriggerEntryValidation.TriggerEntryValidationPage"
Padding="50">
<StackLayout>
<Entry Placeholder="Enter a System.Double">
<Entry.Triggers>
<EventTrigger Event="TextChanged">
<toolkit:NumericValidationAction />
</EventTrigger>
</Entry.Triggers>
</Entry>
</StackLayout>
</ContentPage>
每當文本更改時,都會調用NumericValidationAction的Invoke方法。
螢幕截圖顯示了iOS和Windows 10移動裝置的有效數字條目,但Android裝置中的數字無效:
不幸的是,這在通用Windows平台上不能正常工作:如果在條目中鍵入了無效的數字,則隻有當條目失去輸入焦點時,文本才會變為紅色。 但是,它在其他Windows運作時平台(Windows 8.1和Windows Phone 8.1)上運作良好。