更多事件触发器
前一章关于动画的章节展示了一个按钮,它在点击时旋转或缩放。 虽然大多数动画示例都是为了制作有趣的演示而采取极端措施,但是按钮用一点动画来响应点击并不是不合理的。 这是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)上运行良好。