From e1722a24a41ea5b6f57a52d7d27bd00764a31457 Mon Sep 17 00:00:00 2001 From: kamu Date: Sun, 18 Jun 2017 22:42:28 +0900 Subject: [PATCH 1/5] Pre Release 0.3.0-pre --- .../AiForms.Effects.Droid.csproj | 2 + AiForms.Effects.Droid/BorderPlatformEffect.cs | 105 ++++++++ .../AiForms.Effects.iOS.csproj | 2 + AiForms.Effects.iOS/BorderPlatformEffect.cs | 81 +++++++ AiForms.Effects/AiForms.Effects.csproj | 2 + AiForms.Effects/Border.cs | 105 ++++++++ README.md | 43 ++++ .../AiEffects.TestApp.csproj | 7 + .../ViewModels/BorderPageViewModel.cs | 31 +++ .../AiEffects.TestApp/Views/BorderPage.xaml | 229 ++++++++++++++++++ .../Views/BorderPage.xaml.cs | 15 ++ .../AiEffects.TestApp/Views/MainPage.xaml | 4 + nuget/AiEffects_mac.nuspec | 10 +- 13 files changed, 631 insertions(+), 5 deletions(-) create mode 100644 AiForms.Effects.Droid/BorderPlatformEffect.cs create mode 100644 AiForms.Effects.iOS/BorderPlatformEffect.cs create mode 100644 AiForms.Effects/Border.cs create mode 100644 Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/BorderPageViewModel.cs create mode 100644 Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml create mode 100644 Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml.cs diff --git a/AiForms.Effects.Droid/AiForms.Effects.Droid.csproj b/AiForms.Effects.Droid/AiForms.Effects.Droid.csproj index 481e25b..0ee93b3 100644 --- a/AiForms.Effects.Droid/AiForms.Effects.Droid.csproj +++ b/AiForms.Effects.Droid/AiForms.Effects.Droid.csproj @@ -15,6 +15,7 @@ Assets true + 0.0.5-pre true @@ -92,6 +93,7 @@ + diff --git a/AiForms.Effects.Droid/BorderPlatformEffect.cs b/AiForms.Effects.Droid/BorderPlatformEffect.cs new file mode 100644 index 0000000..2931317 --- /dev/null +++ b/AiForms.Effects.Droid/BorderPlatformEffect.cs @@ -0,0 +1,105 @@ +using System; +using AiForms.Effects; +using AiForms.Effects.Droid; +using Android.Graphics.Drawables; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; + +[assembly: ExportEffect(typeof(BorderPlatformEffect), nameof(Border))] +namespace AiForms.Effects.Droid +{ + public class BorderPlatformEffect : PlatformEffect + { + Android.Views.View _view; + GradientDrawable _border; + Android.Graphics.Color _color; + int _width; + float _radius; + Drawable _orgDrawable; + + protected override void OnAttached() + { + _view = Control ?? Container; + + _border = new GradientDrawable(); + _orgDrawable = _view.Background; + + UpdateRadius(); + UpdateWidth(); + UpdateColor(); + UpdateBorder(); + } + + protected override void OnDetached() + { + var renderer = Container as IVisualElementRenderer; + if (renderer?.Element != null) { // Check disposed + _view.Background = _orgDrawable; + if (Control == null) { + _view.SetPadding(0, 0, 0, 0); + _view.ClipToOutline = false; + } + } + _border?.Dispose(); + _border = null; + _view = null; + } + + protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args) + { + base.OnElementPropertyChanged(args); + if (args.PropertyName == Border.RadiusProperty.PropertyName) { + UpdateRadius(); + UpdateBorder(); + } + else if (args.PropertyName == Border.WidthProperty.PropertyName) { + UpdateWidth(); + UpdateBorder(); + } + else if (args.PropertyName == Border.ColorProperty.PropertyName) { + UpdateColor(); + UpdateBorder(); + } + } + + void UpdateRadius() + { + _radius = Container.Context.ToPixels(Border.GetRadius(Element)); + } + + void UpdateWidth() + { + _width = (int)Container.Context.ToPixels(Border.GetWidth(Element)); + } + + void UpdateColor() + { + _color = Border.GetColor(Element).ToAndroid(); + } + + void UpdateBorder() + { + _border.SetStroke(_width, _color); + _border.SetCornerRadius(_radius); + + var formsBack = (Element as Xamarin.Forms.View).BackgroundColor; + if (formsBack != Xamarin.Forms.Color.Default) { + _border.SetColor(formsBack.ToAndroid()); + } + + if (Element is BoxView) { + var foreColor = ((BoxView)Element).Color; + if (foreColor != Xamarin.Forms.Color.Default) { + _border.SetColor(foreColor.ToAndroid()); + } + } + + if (Control == null) { + _view.SetPadding(_width, _width, _width, _width); + _view.ClipToOutline = true; //not to overflow children + } + _view.SetBackground(_border); + + } + } +} diff --git a/AiForms.Effects.iOS/AiForms.Effects.iOS.csproj b/AiForms.Effects.iOS/AiForms.Effects.iOS.csproj index 930f236..fbfe54d 100644 --- a/AiForms.Effects.iOS/AiForms.Effects.iOS.csproj +++ b/AiForms.Effects.iOS/AiForms.Effects.iOS.csproj @@ -9,6 +9,7 @@ AiForms.Effects.iOS AiForms.Effects.iOS Resources + 0.0.5-pre true @@ -70,6 +71,7 @@ + diff --git a/AiForms.Effects.iOS/BorderPlatformEffect.cs b/AiForms.Effects.iOS/BorderPlatformEffect.cs new file mode 100644 index 0000000..9ce1eb8 --- /dev/null +++ b/AiForms.Effects.iOS/BorderPlatformEffect.cs @@ -0,0 +1,81 @@ +using System; +using AiForms.Effects; +using AiForms.Effects.iOS; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; +using System.Linq; + +[assembly: ExportEffect(typeof(BorderPlatformEffect), nameof(Border))] +namespace AiForms.Effects.iOS +{ + public class BorderPlatformEffect : PlatformEffect + { + UIView _view; + Type[] hasBorderTypes = new Type[]{ + typeof(Entry), + typeof(DatePicker), + typeof(TimePicker), + typeof(Picker), + }; + bool _clipsToBounds; + + protected override void OnAttached() + { + _view = Control ?? Container; + + _clipsToBounds = _view.ClipsToBounds; + if (hasBorderTypes.Any(x => x == Element.GetType())) { + var textfield = _view as UITextField; + textfield.BorderStyle = UITextBorderStyle.None; + } + + UpdateRadius(); + UpdateWidth(); + UpdateColor(); + } + + protected override void OnDetached() + { + if (hasBorderTypes.Any(x => x == Element.GetType())) { + var textfield = _view as UITextField; + textfield.BorderStyle = UITextBorderStyle.RoundedRect; + } + _view.ClipsToBounds = _clipsToBounds; + _view.Layer.CornerRadius = 0f; + _view.Layer.BorderWidth = 0; + _view = null; + } + + protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args) + { + base.OnElementPropertyChanged(args); + if (args.PropertyName == Border.RadiusProperty.PropertyName) { + UpdateRadius(); + } + else if (args.PropertyName == Border.WidthProperty.PropertyName) { + UpdateWidth(); + } + else if (args.PropertyName == Border.ColorProperty.PropertyName) { + UpdateColor(); + } + } + + void UpdateRadius() + { + var r = Border.GetRadius(Element); + _view.Layer.CornerRadius = (nfloat)r; + _view.ClipsToBounds = true; + } + + void UpdateWidth() + { + _view.Layer.BorderWidth = (float)Border.GetWidth(Element); + } + + void UpdateColor() + { + _view.Layer.BorderColor = Border.GetColor(Element).ToCGColor(); + } + } +} diff --git a/AiForms.Effects/AiForms.Effects.csproj b/AiForms.Effects/AiForms.Effects.csproj index c2aa927..fcbeef6 100644 --- a/AiForms.Effects/AiForms.Effects.csproj +++ b/AiForms.Effects/AiForms.Effects.csproj @@ -11,6 +11,7 @@ AiForms.Effects v4.5 Profile259 + 0.0.5-pre true @@ -35,6 +36,7 @@ + diff --git a/AiForms.Effects/Border.cs b/AiForms.Effects/Border.cs new file mode 100644 index 0000000..f5d176e --- /dev/null +++ b/AiForms.Effects/Border.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using Xamarin.Forms; + +namespace AiForms.Effects +{ + public static class Border + { + public static readonly BindableProperty OnProperty = + BindableProperty.CreateAttached( + propertyName: "On", + returnType: typeof(bool), + declaringType: typeof(Border), + defaultValue: false, + propertyChanged: OnOffChanged + ); + + public static void SetOn(BindableObject view, bool value) + { + view.SetValue(OnProperty, value); + } + + public static bool GetOn(BindableObject view) + { + return (bool)view.GetValue(OnProperty); + } + + private static void OnOffChanged(BindableObject bindable, object oldValue, object newValue) + { + var view = bindable as View; + if (view == null) + return; + + if ((bool)newValue) { + view.Effects.Add(new BorderRoutingEffect()); + } + else { + var toRemove = view.Effects.FirstOrDefault(e => e is BorderRoutingEffect); + if (toRemove != null) + view.Effects.Remove(toRemove); + } + } + + public static readonly BindableProperty RadiusProperty = + BindableProperty.CreateAttached( + "Radius", + typeof(double), + typeof(Border), + default(double) + ); + + public static void SetRadius(BindableObject view, double value) + { + view.SetValue(RadiusProperty, value); + } + + public static double GetRadius(BindableObject view) + { + return (double)view.GetValue(RadiusProperty); + } + + public static readonly BindableProperty WidthProperty = + BindableProperty.CreateAttached( + "Width", + typeof(double), + typeof(Border), + default(double) + ); + + public static void SetWidth(BindableObject view, double value) + { + view.SetValue(WidthProperty, value); + } + + public static double GetWidth(BindableObject view) + { + return (double)view.GetValue(WidthProperty); + } + + public static readonly BindableProperty ColorProperty = + BindableProperty.CreateAttached( + "Color", + typeof(Color), + typeof(Border), + Color.Transparent + ); + + public static void SetColor(BindableObject view, Color value) + { + view.SetValue(ColorProperty, value); + } + + public static Color GetColor(BindableObject view) + { + return (Color)view.GetValue(ColorProperty); + } + + class BorderRoutingEffect : RoutingEffect + { + public BorderRoutingEffect() : base("AiForms." + nameof(Border)) { } + } + + + } +} diff --git a/README.md b/README.md index d8ba7f1..6e02967 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Xamarin.Forms Effects for Android / iOS only. ## Features +* [Border](#border) + * add border to a view. * [AddText](#addtext) * add one line Text to a view. * [ToFlatButton](#toflatbutton) @@ -44,6 +46,47 @@ public override bool FinishedLaunching(UIApplication app, NSDictionary options) } ``` +## Border + +This is the effect that add border to a view. +Entry, Picker, DatePicker and TimePicker on iOS have a border by default. +When specifying their width 0, it is possible that hide border. + +### Parameters + +* On + * Effect On/Off (true is On) +* Width + * Border width (default 0) +* Color + * Border color (default transparent) +* Radius + * Border radius (default 0) + +### How to write by Xaml + +```xml + + + + +``` + +### Limitations + +* On Android Entry, Picker, DatePicker, TimePicker's input underline is hidden if this effect attached. +* On Android, Button is not displayed correctly. Use [ToFlatButton](#toflatbutton) for button. +* On Android WebView, Frame, ScrollView are not supported. +* On Android ListView and TableView overflow background from border. +* Using AddCommand simultaneously is not supported. + ## AddText This is the effect that add one line text into a view. diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/AiEffects.TestApp.csproj b/Tests/AiEffects.TestApp/AiEffects.TestApp/AiEffects.TestApp.csproj index bf07c25..ce18a56 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/AiEffects.TestApp.csproj +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/AiEffects.TestApp.csproj @@ -57,6 +57,10 @@ AddTextPage.xaml + + BorderPage.xaml + + @@ -80,6 +84,9 @@ MSBuild:UpdateDesignTimeXaml + + MSBuild:UpdateDesignTimeXaml + diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/BorderPageViewModel.cs b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/BorderPageViewModel.cs new file mode 100644 index 0000000..42f716c --- /dev/null +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/BorderPageViewModel.cs @@ -0,0 +1,31 @@ +using System; +using Prism.Mvvm; +using Reactive.Bindings; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace AiEffects.TestApp.ViewModels +{ + public class BorderPageViewModel : BindableBase + { + public ReactiveProperty EffectOn { get; } = new ReactiveProperty(false); + public ReactiveProperty Radius { get; } = new ReactiveProperty(); + public ReactiveProperty Width { get; } = new ReactiveProperty(); + public ReactiveProperty WidthToggle { get; } = new ReactiveProperty(true); + public ReactiveProperty RadiusToggle { get; } = new ReactiveProperty(true); + public ReactiveProperty BorderColor { get; } = new ReactiveProperty(); + + public BorderPageViewModel() + { + BorderColor.Value = Color.Blue; + + WidthToggle.Subscribe(x => { + Width.Value = x ? 2.0d : 0.0d; + }); + + RadiusToggle.Subscribe(x => { + Radius.Value = x ? 8.0d : 0.0d; + }); + } + } +} diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml new file mode 100644 index 0000000..6ccaf2d --- /dev/null +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml.cs b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml.cs new file mode 100644 index 0000000..8a5c9d0 --- /dev/null +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/BorderPage.xaml.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +using Xamarin.Forms; + +namespace AiEffects.TestApp.Views +{ + public partial class BorderPage : ContentPage + { + public BorderPage() + { + InitializeComponent(); + } + } +} diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/MainPage.xaml b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/MainPage.xaml index 876e0ca..59773d9 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/MainPage.xaml +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/MainPage.xaml @@ -43,5 +43,9 @@