Hey,
today I accomplished the task to create Gradient Background without the use of an Image.
The task was to make it possible for the user to change the Background of the Views on Runtime! First I thought I could achive this with Images but on the long run it would have blown up the App-Size.
This is the best implementation I could come up with and the choosen Colors are persistent with the usage of the Settings Plugin form @JamesMontemagno that you can get here: Nuget
This code works for Android and iOS and you can drop it in if you want
So long Story short here's the code:
CustomRenderer in PCL
:
public class GradientContentPage : ContentPage
{
public static BindableProperty StartColorProperty = BindableProperty.Create<GradientContentPage, Color>(p => p.StartColor, Color.White);
public static BindableProperty EndColorProperty = BindableProperty.Create<GradientContentPage, Color>(p => p.EndColor, Color.Gray);
public Color StartColor
{
get { return (Color) GetValue(StartColorProperty); }
set { SetValue(StartColorProperty, value); }
}
public Color EndColor
{
get { return (Color) GetValue(EndColorProperty); }
set { SetValue(EndColorProperty, value); }
}
}
To make the Colors available all over my app I create two static variables in App.cs and since I can't put a Color
into the Settings I have to store them as string
So here's the code for my App.cs
public static Color StartColor;
public static Color EndColor;
public App()
{
StartColor = StringToColor(Helpers.Settings.StartColor.Split(' , ');
EndColor = StringToColor(Helpers.Settings.EndColor.Split(' , ');
MainPage = new NavigationPage(new DashboardView{ StartColor = App.StartColor, EndColor = App.EndColor });
}
As you may notice I've setting the Colors for the View on creation. Sadly you have to this, because Views that inherit from ContentPage
can't make us of Implicit Styles
right now because of this Bug 27659
So anyway let's take a look at StringToColor
private static void StringToColor(IList<string> color)
{
for(var i = 0; i < color.Count(); i++)
{
//Regex to get the color code
color[i] = Regex.Replace(color[i], @"^\d.\d+]", "");
}
var a = double.Parse(color[0], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
var r = double.Parse(color[1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
var g = double.Parse(color[2], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
var b = double.Parse(color[3], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
return Color.FromRgba(r, g, b, a);
}
Ok, I admit I'm not happy with this function and if anyone has a better idea how to achive this, please let me know! Basically this function find the Color-Code.
Alright now let's get to the platform renderers:
CustomRenderer in Android
:
[assembly: ExportRenderer(typeof(GradientContentPage), typeof(GradientContentPageRenderer))]
namespace YourApp.Droid
{
public class GradientContentPageRenderer : PageRenderer
{
protected override void OnVisibilityChanged(Android.Views.View changedView, ViewStates visibility)
{
base.OnVisibilityChanged(changedView, visibility);
SetBackground();
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> page)
{
base.OnElementChanged(page);
SetBackground();
}
private void SetBackground()
{
var startColor = App.StartColor.ToAndroid();
var endColor = App.EndColor.ToAndroid();
var colors = new int[] { startColor, endColor };
Background = new GradientDrawable(GradientDrawable.Orientation.TopBottom, colors);
}
}
}
You have to overwrite both Events to get every Background in the color you wan't. It's a bit odd but this is how Forms on Android reacts and there is no (at least I haven't found one) event that gets fired everytime you switch a Page;
CustomRenderer in iOS
:
[assembly: ExportRenderer(typeof(GradientContentPage), typeof(GradientContentPageRenderer))]
namespace YourApp.iOS
{
public class GradientContentPageRenderer: PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var gradientLayer = new CAGradientLayer
{
Frame = View.Bounds,
Colors = new [] { App.StartColor.ToCGColor(), App.EndColor.ToCGColor() },
StartPoint = new CGPoint(0, 0),
EndPoint = new CGPoint(1, 1)
}
//This is needed to get every background redrawn if the color changes on runtime
if(View.Layer.Sublayers[0].GetType() == typeof(CAGradientLayer))
{
View.Layer.ReplaceSublayer(View.Layer.Sublayers[0], gradientLayer);
}
else
{
View.Layer.InsertSublayer(gradientLayer, 0);
}
}
}
}
For this one I have to thank @JohnBeans for his thread here on the forum: Click me
So for the Color Picking I created a view where the user can select a color and then it gets automatically drawn on runtime with the new color. But posting this would be to much for one thread. If someone is interested I can post it.