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

简介: 触发动作和动画虽然某些触发器可以完全在XAML中实现,但其他触发器需要一些代码支持。 如您所知,Xamarin.Forms没有直接支持在XAML中实现动画,因此如果您想使用触发器为元素设置动画,则需要一些代码。

触发动作和动画
虽然某些触发器可以完全在XAML中实现,但其他触发器需要一些代码支持。 如您所知,Xamarin.Forms没有直接支持在XAML中实现动画,因此如果您想使用触发器为元素设置动画,则需要一些代码。
有几种方法可以从XAML调用动画。 最明显的方法是使用EventTrigger,它定义了两个属性:

  • Event 类型是 string。
  • Actions 类型是 IList。

当您附加触发器的元素触发该特定事件时,EventTrigger将调用Actions集合中的所有TriggerAction对象。
例如,VisualElement定义了与输入焦点相关的两个事件:聚焦和未聚焦。您可以将这些事件名称设置为两个不同EventTrigger对象的Event属性。当元素触发事件时,将调用TriggerAction类型的对象。你的工作是提供一个派生自TriggerAction的类。此派生类重写名为Invoke的方法以响应事件。
Xamarin.Forms定义了TriggerAction类和TriggerAction 类,但这两个类都是抽象的。通常,您将从TriggerAction 派生并将type参数设置为触发器操作可以支持的最通用的类​​。
例如,假设您要从TriggerAction 派生一个调用ScaleTo的类来为Scale属性设置动画。将type参数设置为VisualElement,因为这是ScaleTo扩展方法引用的类。该类型的对象也传递给Invoke。
按照惯例,从TriggerAction派生的类在其名称中有一个Action后缀。这样的类可以这么简单:

public class ScaleAction : TriggerAction<VisualElement>
{
    protected override void Invoke(VisualElement visual)
    {
        visual.ScaleTo(1.5);
    }
}

当您将此类包含在附加到Entry视图的EventTrigger中时,特定的Entry对象将作为参数传递给Invoke方法,该方法使用ScaleTo为该Entry对象设置动画。 Entry在默认的四分之一秒内扩展到原始大小的150%。
当然,您可能不希望使该类具体。 对于Focused事件,这个简单的ScaleAction类可以正常工作,但是对于Unfocused事件,您需要一个不同的类来将Scale属性设置为1。
您的Action 派生可以包含使类非常通用的属性。 您甚至可以使ScaleAction类如此通用,使其基本上成为ScaleTo方法的包装器。 这是Xamarin.FormsBook.Toolkit库中的ScaleAction版本:

namespace Xamarin.FormsBook.Toolkit
{
    public class ScaleAction : TriggerAction<VisualElement>
    {
        public ScaleAction()
        {
            // Set defaults.
            Anchor = new Point (0.5, 0.5);
            Scale = 1;
            Length = 250;
            Easing = Easing.Linear;
        }
        public Point Anchor { set; get; }
        public double Scale { set; get; }
        public int Length { set; get; }
        [TypeConverter(typeof(EasingConverter))]
        public Easing Easing { set; get; }
        protected override void Invoke(VisualElement visual)
        {
            visual.AnchorX = Anchor.X;
            visual.AnchorY = Anchor.Y;
            visual.ScaleTo(Scale, (uint)Length, Easing);
        }
    }
}

您可能想知道是否应该使用可绑定属性来支持这些属性,以便它们可以成为数据绑定的目标。 但是,您可以这样做,因为TriggerAction派生自Object而不是BindableObject。 保持属性简单。
请注意Easing属性上的TypeConverter属性。 此Easing属性可能在XAML中设置,但XAML解析器不知道如何将文本字符串转换为“输入”和“输出”类型的对象。 以下自定义类型转换器(也在Xamarin.Forms Book.Toolkit中)帮助XAML解析器将文本字符串转换为Easing对象:

namespace Xamarin.FormsBook.Toolkit
{
    public class EasingConverter : TypeConverter
    {
        public override bool CanConvertFrom(Type sourceType)
        {
            if (sourceType == null)
                throw new ArgumentNullException("EasingConverter.CanConvertFrom: sourceType");
            return (sourceType == typeof(string));
        }
        public override object ConvertFrom(CultureInfo culture, object value)
        {
            if (value == null || !(value is string))
                return null;
            string name = ((string)value).Trim();
            if (name.StartsWith("Easing"))
            {
                name = name.Substring(7);
            }
 
            FieldInfo field = typeof(Easing).GetRuntimeField(name);
            if (field != null && field.IsStatic)
            {
                return (Easing)field.GetValue(null);
            }
            throw new InvalidOperationException(
            String.Format("Cannot convert \"{0}\" into Xamarin.Forms.Easing", value));
        }
    }
}

EntrySwell程序在其资源字典中定义了一个隐含的条目样式。 该Style在其Triggers集合中有两个EventTrigger对象,一个用于Focused,另一个用于Unfocused。 两者都调用ScaleAction但具有不同的属性设置:

<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="EntrySwell.EntrySwellPage"
             Padding="20, 50, 120, 0">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Entry">
                <Style.Triggers>
                    <EventTrigger Event="Focused">
                        <toolkit:ScaleAction Anchor="0, 0.5"
                                             Scale="1.5"
                                             Easing="SpringOut" />
                    </EventTrigger>
                    <EventTrigger Event="Unfocused">
                        <toolkit:ScaleAction Anchor="0, 0.5"
                                             Scale="1" />
                    </EventTrigger>
                </Style.Triggers>
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout Spacing="20">
        <Entry Placeholder="enter name" />
        <Entry Placeholder="enter address" />
     
        <Entry Placeholder="enter city and state" />
    </StackLayout>
</ContentPage>

请注意,EventTrigger不需要TargetType属性。 EventTrigger定义的唯一构造函数没有参数。
当每个条目获得输入焦点时,您会看到它变大,然后短暂地超过1.5 Scale值。 这就是SpringOut缓动函数的效果。
如果您想使用自定义缓动功能怎么办? 当然,您需要在代码中定义这样的缓动函数,并且可以在代码隐藏文件中执行此操作。 但是你会怎么引用呢?
XAML中的缓动功能? 这是如何做:
首先,从XAML文件中删除ResourceDictionary标记。 这些标记实例化ResourceDictionary并将其设置为Resources属性。
其次,在代码隐藏文件的构造函数中,实例化ResourceDictionary并将其设置为Resources属性。 在InitializeComponent之前执行此操作,以便在解析XAML文件时它存在:

Resources = new ResourceDictionary();
InitializeComponent();

第三,在这两个语句之间,将一个带有自定义缓动函数的Easing对象添加到Resources字典中:

Resources = new ResourceDictionary();
Resources.Add("customEase", new Easing(t => -6 * t * t + 7 * t));
InitializeComponent();

这个二次公式将0映射到0和1到1,但是0.5到2,因此很明显动画是否正确使用了这个缓动函数。
最后,在EventTrigger定义中引用使用StaticResource的字典条目:

<EventTrigger Event="Focused">
    <toolkit:ScaleAction Anchor="0, 0.5"
                         Scale="1.5"
                         Easing="{StaticResource customEase}" />
</EventTrigger>

因为Resources字典中的对象是Easing类型,所以XAML解析器将它直接分配给ScaleAction的Easing属性并绕过TypeConverter。
本章的代码示例中有一个名为CustomEasingSwell的解决方案,它演示了这种技术。
不要使用DynamicResource将自定义Easing对象设置为Easing属性,可能希望稍后在代码中定义缓动函数。 DynamicResource要求target属性由可绑定属性支持; StaticResource没有。
您已经了解了如何使用Trigger设置属性以响应属性更改,而EventTrigger则调用TriggerAction对象以响应事件触发。
但是如果你想调用TriggerAction以响应属性更改呢? 也许您想从XAML调用动画,但EventTrigger没有适当的事件。
还有第二种方法可以调用涉及常规Trigger类而不是EventTrigger的TriggerAction派生。 如果查看TriggerBase(所有其他触发器类派生的类)的文档,您将看到以下两个属性:

  • EnterActions 类型是 IList
  • ExitActions 类型是 IList

与Trigger一起使用时,当Trigger条件变为true时,将调用EnterActions集合中的所有TriggerAction对象,并在条件再次变为false时调用ExitActions集合中的所有对象。
EnterExitSwell程序演示了这种技术。 它使用Trigger监视IsFocused属性,并调用两个ScaleAction实例,以在IsFocused变为True时增加Entry的大小,并在IsFocused停止为True时减小Entry的大小:

<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="EnterExitSwell.EnterExitSwellPage"
             Padding="20, 50, 120, 0">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Entry">
                <Style.Triggers>
                    <Trigger TargetType="Entry" Property="IsFocused" Value="True">
                        <Trigger.EnterActions>
                            <toolkit:ScaleAction Anchor="0, 0.5"
                                                 Scale="1.5"
                                                 Easing="SpringOut" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <toolkit:ScaleAction Anchor="0, 0.5"
                                                 Scale="1" />
                        </Trigger.ExitActions>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout Spacing="20">
        <Entry Placeholder="enter name" />
        <Entry Placeholder="enter address" />
        <Entry Placeholder="enter city and state" />
    </StackLayout>
</ContentPage>

总之,您可以通过使用Trigger或使用EventTrigger触发事件来调用从TriggerAction 派生的类。
但是不要在EventTrigger中使用EnterActions和ExitActions。 EventTrigger仅调用其Actions集合中的TriggerAction对象。

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