第二十三章:触发器和行为(四)

简介: 更多事件触发器前一章关于动画的章节展示了一个按钮,它在点击时旋转或缩放。 虽然大多数动画示例都是为了制作有趣的演示而采取极端措施,但是按钮用一点动画来响应点击并不是不合理的。 这是EventTrigger的完美工作。

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

这是三个按钮,因为它们的大小随着点击而增长:
2019_03_31_232508
是否有可能在这里使用两个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设备中的数字无效:
2019_03_31_235606
不幸的是,这在通用Windows平台上不能正常工作:如果在条目中键入了无效的数字,则只有当条目失去输入焦点时,文本才会变为红色。 但是,它在其他Windows运行时平台(Windows 8.1和Windows Phone 8.1)上运行良好。

目录
相关文章
|
JavaScript Android开发
第二十三章:触发器和行为(十二)
淡化和定向在本书中,您已经看到了几个颜色选择程序,可以通过使用三个Slider元素以交互方式形成颜色。 本章的最后一个示例是另一个颜色选择程序,但是这个程序为您提供了选项:它包含三个标记为“RGB Hex”,“RGB Float”和“HSL”的单选按钮(实际上是简单的Label元素)。
707 0
|
JavaScript Android开发
第二十三章:触发器和行为(七)
行为 触发器和行为通常是串联讨论的,因为它们具有一些应用重叠。 有时候你会感到困惑是否使用触发器或行为,因为似乎要么这样做工作。你可以用触发器做任何事情,你也可以做一个行为。 但是,行为总是涉及一些代码,这是一个派生自Behavior 的类。
946 0
|
JavaScript Android开发
第二十三章:触发器和行为(十一)
单选按钮内置于旧汽车仪表板中的无线电通常具有一排六个(左右)按钮,可以为各种无线电台“编程”。 按下其中一个按钮会导致无线电跳转到该预选电台,并且还会弹出前一个选择按钮。那些旧的汽车收音机现在是古董,但我们的电脑屏幕上的互斥选项仍然由我们称为单选按钮的视觉对象表示。
908 0
|
JavaScript Android开发
第二十三章:触发器和行为(十)
响应水龙头切换视图的各种表现形式演示了一种响应XAML文件中的点击的方法。 如果将tap事件集成到VisualElement类中,您可以使用EventTrigger更直接且更轻松地获取它们。 但是您无法将EventTrigger附加到TapGestureRecognizer。
542 0
|
JavaScript Android开发 Windows
第二十三章:触发器和行为(九)
切换和复选框在第15章“交互式界面”和第16章“数据绑定”中,您了解了如何构造传统的CheckBox视图。 但是,自定义视图的另一种方法是将视图的交互逻辑合并到行为中,然后完全在XAML中实现视觉效果。
727 0
|
Android开发
第二十三章:触发器和行为(八)
具有属性的行为Behavior 类派生自Behavior类,该类派生自BindableObject。这表明您的Behavior 派生可以定义自己的可绑定属性。之前你看过一些Action 衍生产品,比如ScaleAction和ShiverAction,它们定义了一些属性以赋予它们更大的灵活性。
649 0
|
JavaScript Android开发
第二十三章:触发器和行为(六)
MultiTrigger中的组合条件Trigger和DataTrigger都有效地监视属性以确定它是否等于特定值。 这称为触发器的条件,如果条件为真,则调用Setter对象的集合。作为程序员,您可能会开始怀疑是否可以在触发器中具有多个条件。
649 0
|
JavaScript Android开发 iOS开发
第二十三章:触发器和行为(五)
数据触发器到目前为止,您只看到在特定对象的上下文中运行的触发器。 触发器通过更改同一对象的另一个属性或通过调用影响该对象的Action来响应对象属性的更改。 EventTrigger同样响应一个对象触发的事件,以在同一个对象上调用Action。
933 0
|
JavaScript Android开发
第二十三章:触发器和行为(三)
触发动作和动画虽然某些触发器可以完全在XAML中实现,但其他触发器需要一些代码支持。 如您所知,Xamarin.Forms没有直接支持在XAML中实现动画,因此如果您想使用触发器为元素设置动画,则需要一些代码。
1011 0
|
JavaScript Android开发
第二十三章:触发器和行为(二)
触发器 在最普遍(和最模糊)的意义上,触发器是导致响应的条件。 更具体地说,触发器通过设置另一个属性或运行一些代码来响应属性更改或触发事件。 几乎总是,设置的属性或运行的代码涉及用户界面,并在XAML中表示。
966 0