I want to add a gradient to both parts of the menu of an Xamarin app. Place where I want the gradient are marked with "here" - I'm just after ToolBar for now...
In order to attempt this, I have created the following class
public class GradientHeaderNavigationPage : NavigationPage
{
public GradientHeaderNavigationPage() { }
public GradientHeaderNavigationPage(Page root) : base(root) { }
public static readonly BindableProperty LeftColorProperty =
BindableProperty.Create(propertyName: nameof(LeftColor),
returnType: typeof(Color),
declaringType: typeof(GradientHeaderNavigationPage),
defaultValue: Color.FromHex("#92CD8C")); //Color.Accent);
public static readonly BindableProperty RightColorProperty =
BindableProperty.Create(propertyName: nameof(RightColor),
returnType: typeof(Color),
declaringType: typeof(GradientHeaderNavigationPage),
defaultValue: Color.FromHex("#17AEC6")); //Color.Accent);
public Color LeftColor
{
get { return (Color)GetValue(LeftColorProperty); }
set { SetValue(LeftColorProperty, value); }
}
public Color RightColor
{
get { return (Color)GetValue(RightColorProperty); }
set { SetValue(RightColorProperty, value); }
}
}
Then for Android (we can worry about iOS later) I have added the following custom renderer
[assembly: ExportRenderer(typeof(GradientHeaderNavigationPage), typeof(GradientHeaderNavigationRenderer))]
namespace Drax.Droid.Renderers
{
public class GradientHeaderNavigationRenderer : NavigationRenderer
{
public GradientHeaderNavigationRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
return;
var control = (GradientHeaderNavigationPage)Element;
var context = (MainActivity)Context;
context.ActionBar.SetBackgroundDrawable(
new GradientDrawable(GradientDrawable.Orientation.RightLeft,
new int[] { control.RightColor.ToAndroid(), control.LeftColor.ToAndroid() }));
}
}
}
and added to the /Resources/drawable
folder the file gradient_header.xml
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="180"
android:startColor="#92CD8C"
android:endColor="#17AEC6"
android:type="linear" />
</shape>
and in /Resources/layout/ToolBar.axml
I have added android:background="@drawable/header_gradient"
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/header_gradient"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
Now, in my App.cs file (I am using Prism for MVVM) I have
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Drax
{
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
try
{
InitializeComponent();
var result = await NavigationService.NavigateAsync(
new Uri("/DraxMasterDetailPage/NavigationPage/MapPage"));
if (!result.Success)
SetMainPageFromException(result.Exception);
}
catch (Exception e)
{
Console.WriteLine($"Android DEBUG:: {e.Message}");
}
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
RegisterServices(containerRegistry);
RegisterViews(containerRegistry);
}
private void RegisterServices(IContainerRegistry containerRegistry)
{
containerRegistry.Register<ILoggerFacade, Services.DebugLogger>();
}
private void RegisterViews(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<DraxMasterDetailPage>();
containerRegistry.RegisterForNavigation<MapPage>();
}
private void SetMainPageFromException(Exception ex)
{
var layout = new StackLayout
{
Padding = new Thickness(40)
};
layout.Children.Add(new Label
{
Text = ex?.GetType()?.Name ?? "Unknown Error encountered",
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center
});
layout.Children.Add(new ScrollView
{
Content = new Label
{
Text = $"{ex}",
LineBreakMode = LineBreakMode.WordWrap
}
});
MainPage = new ContentPage
{
Content = layout
};
}
}
}
with my DraxMasterDetailPage
as
<MasterDetailPage x:Class="Drax.Views.DraxMasterDetailPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Controls="clr-namespace:Drax.Controls">
<MasterDetailPage.Master>
<NavigationPage Title="Drax">
<x:Arguments>
<ContentPage Title="Menu">
<StackLayout Padding="40">
</StackLayout>
</ContentPage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Master>
</MasterDetailPage>
I have tried to change NavigationPage
to GradientHeaderNavigationPage
but this throws a runtime exception of
System.NullReferenceException: Object reference not set to an instance of an object.
on this line in the Adroid renderer
context.ActionBar.SetBackgroundDrawable(
new GradientDrawable(GradientDrawable.Orientation.RightLeft,
new int[] { control.RightColor.ToAndroid(), control.LeftColor.ToAndroid() }));
ActionBar
is null.
How can I amend this code to show the gradient ToolBar?