在 Xamarin.Forms 2.1.0-pre1 ,Xamarin.Forms新增了新的Effects API. Effects是一系列方法,为了给View的渲染器添加运行时改变.
然而,我想强调的是, Effects天生被设计为高可复用的. 如果一个Effect能够解决一个难题, 它可能能够在你的整个APP中使用.如果你写了一个Effect来解决你的一个难题, 你能够分享它给其他遇到同样问题的人. 这篇文章尝试展示一种方式,能够帮助我们使分享Effects这件事变得很简单.
public class ShadowEffect : PlatformEffect { protected override void OnAttached () { UpdateSize (); UpdateColor (); UpdateOpacity (); } protected override void OnDetached () { Container.Layer.ShadowOpacity = 0; } protected override void OnElementPropertyChanged (PropertyChangedEventArgs e) { Debug.WriteLine (e.PropertyName); if (e.PropertyName == ViewExtensions.HasShadowProperty.PropertyName) { UpdateOpacity (); } else if (e.PropertyName == ViewExtensions.ShadowColorProperty.PropertyName) { UpdateColor (); } else if (e.PropertyName == ViewExtensions.ShadowSizeProperty.PropertyName) { UpdateSize (); } } private void UpdateOpacity () { Container.Layer.ShadowOpacity = ViewExtensions.GetHasShadow (Element) ? 1 : 0; } private void UpdateColor () { var color = ViewExtensions.GetShadowColor (Element); Container.Layer.ShadowColor = color.ToCGColor (); } private void UpdateSize () { Container.Layer.ShadowRadius = (nfloat)ViewExtensions.GetShadowSize (Element); } }
上面的代码实在是太多了,我们可以使用下面的方式:
public class ShadowEffect : PlatformEffect { protected override void OnAttached () { Container.Layer.ShadowOpacity = 1; Container.Layer.ShadowColor = UIColor.Black.ToCGColor; Container.Layer.ShadowRadius = 6; } protected override void OnDetached () { Container.Layer.ShadowOpacity = 0; } }
使用这种方式来写Effects非常简单,但是同时也导致我们无法方便的配置它和重用它.这种方式更像前面学习的 CustomRenderer .
ShadowEffect继承自PlatformEffect,位于特定平台的项目中. 即将custom renderers,PlatformEffects的实现是位于特定平台的项目中,然而Effect API是完全跨平台的,只不过在特定平台中使用不同的参数实现了 PlatformEffect<T, T>
.一个主要的不同点就是, Effects 不包含它附加的Container/Control/Element的类型信息,这是因为它们能够被附加到任何的要素(Element).当Effect附加到一个不支持的要素(Element)时,会gracefully degrade或者抛出异常.
如果库中包含Effect,有两个重要的属性需要设置:
[assembly: ResolutionGroupName ("你的公司名称")]
: 用于为你的Effects设置一个公司名称来防止与其它同名的Effects发生冲突.你可以在多个程序集中设置同一公司名称.[assembly: ExportEffect (typeof (ShadowEffect), "ShadowEffect")]
: 用于给你的Effect设置唯一ID,通过上面的公司名称和该唯一ID来定位Effect.
简单用法:
给你的View添加一个Effect非常简单:
var button = new Button { Text = "I have a shadow" }; button.Effects.Add (Effect.Resolve ("YourCompany.ShadowEffect"));
如果你没有为特定平台实现一个Effect. Effect.Resolve方法会返回一个非NULL值, 该值不会产生任何影响.这种设置对于只想某一平台而不是全部平台添加Effect非常的有用, 但是会有极小的内存损耗代价.
public static class ViewEffects { public static readonly BindableProperty HasShadowProperty = BindableProperty.CreateAttached ("HasShadow", typeof (bool), typeof (ViewEffects), false, propertyChanged: OnHasShadowChanged); private static void OnHasShadowChanged (BindableObject bindable, object oldValue, object newValue) { var view = bindable as View; if (view == null) return; var hasShadow = (bool)newValue; if (hasShadow) { view.Effects.Add (new ShadowEffect ()); } else { var toRemove = view.Effects.FirstOrDefault (e => e is ShadowEffect); if (toRemove != null) view.Effects.Remove (toRemove); } } public static readonly BindableProperty ShadowSizeProperty = BindableProperty.CreateAttached ("ShadowSize", typeof (double), typeof (ViewEffects), 0d); public static readonly BindableProperty ShadowColorProperty = BindableProperty.CreateAttached ("ShadowColor", typeof (Color), typeof (ViewEffects), Color.Default); public static void SetHasShadow (BindableObject view, bool hasShadow) { view.SetValue (HasShadowProperty, hasShadow); } public static bool GetHasShadow (BindableObject view) { return (bool)view.GetValue (HasShadowProperty); } public static void SetShadowSize (BindableObject view, double size) { view.SetValue (ShadowSizeProperty, size); } public static double GetShadowSize (BindableObject view) { return (double)view.GetValue (ShadowSizeProperty); } public static void SetShadowColor (BindableObject view, Color color) { view.SetValue (ShadowColorProperty, color); } public static Color GetShadowColor (BindableObject view) { return (Color)view.GetValue (ShadowColorProperty); } class ShadowEffect : RoutingEffect { public ShadowEffect () : base ("Xamarin.ShadowEffect") { } } }
上面看上去有很多的代码,实际上只有三个附加属性(attached BindableProperty
s)和几个静态的getter和 setter.唯一有点复杂的代码是OnHasShadowChanged,它里面根据附加属性的值来简单的添加或者移除Effect
. 最后,代码中使用了RoutingEffect而不是直接调用Effect.Resolve方法,只是为了使分离过程更加简单,因为该方法并没有获取特定平台的类型信息的编译时间.
Effect的使用方法:
<Button local:ViewEffects.HasShadow="True" local:ViewEffects.ShadowColor="#222222" local:ViewEffects.ShadowSize="4" />
或者更好的方式,在Style中使用Effect,这样你可以将Effect应用到任何/全部的Button上:
<Style TargetType="Button"> <Style.Setters> <Setter Property="local:ViewExtensions.HasShadow" Value="True" /> <Setter Property="local:ViewExtensions.ShadowColor" Value="#232343" /> <Setter Property="local:ViewExtensions.ShadowSize" Value="5" /> </Style.Setters> </Style>
原文地址:http://xfcomplete.net/general/2016/01/20/using-effects/
个人理解(16/2/1):由于Forms中官方的控件的属性非常的少,很多时候,我们需要用到更多的属性,在Effects出现之前,我们只能使用CustomRenderer来在特定平台中重写某一个控件的渲染类(ViewRenderer),譬如如果想给控件View_A添加属性Pro_A,就要继承View_A来写一个子类,另外在特定平台中写Renderer,使用的时候还需要在xaml中引用<vewis:ExtendView_A
如果使用Effect,我们可以直接写两个Effect,在官方控件<View_A 的基础上添加Effect就可以实现,而且该Effect完全是可以通用的,只要该View支持该属性,该Effect完全可以使用在不同的View中去,更别谈有Style这种工具可以全局或者局部设置样式了
原文:http://www.cnblogs.com/yz1311/p/5176446.html