如何在Android Xamarin App中创建渐变工具栏

时间:2019-06-01 14:24:25

标签: android xamarin

我想为Xamarin应用程序菜单的两个部分添加渐变。我要渐变的地方标有“这里”-现在我只是在ToolBar之后...

enter image description here

为了尝试这一点,我创建了以下类

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); }
    }
}

然后用于Android(我们稍后可能会担心iOS)我添加了以下自定义渲染器

[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() }));
        }
    }
}

并将文件/Resources/drawable添加到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>

并且在/Resources/layout/ToolBar.axml中我添加了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" />

现在,在我的App.cs文件中(我将棱镜用于MVVM)

[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
            };
        }
    }
}

我的DraxMasterDetailPage

<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>

我尝试将NavigationPage更改为GradientHeaderNavigationPage,但这会引发

的运行时异常
  

System.NullReferenceException:对象引用未设置为对象的实例。

在Adroid渲染器的这一行

context.ActionBar.SetBackgroundDrawable(
    new GradientDrawable(GradientDrawable.Orientation.RightLeft, 
    new int[] { control.RightColor.ToAndroid(), control.LeftColor.ToAndroid() }));

ActionBar为空。

如何修改此代码以显示渐变工具栏?

2 个答案:

答案 0 :(得分:1)

您已通过gradient_header.xmlToolBar.axml更改了工具栏的背景。这意味着整个应用程序的工具栏已更改。您可以注释掉Naviagtion渲染器以查看效果。

如果要使用代码进行调整,则应使用ContentPage的自定义渲染器,而不要使用NavigationPage的渲染器。

首先,使用MainActivity中的代码定义一些方法来找到工具栏:

private static MainActivity instance;

public static View RootFindViewById<T>(int id) where T : View
{
    return instance.FindViewById<T>(id);
}

public MainActivity()
{
    instance = this;
}

然后您可以动态更改工具栏的背景:

[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
namespace PrismDemo.Droid
{
    public class CustomPageRenderer : PageRenderer
    {
        public CustomPageRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
        {
            base.OnElementChanged(e);

            var toolbar = MainActivity.RootFindViewById<Toolbar>(Resource.Id.toolbar);
            if (toolbar == null) return;

            toolbar.SetBackground(new GradientDrawable(GradientDrawable.Orientation.RightLeft,
                new int[] { Color.Red.ToAndroid(), Color.Blue.ToAndroid() }));
        }
    }
}

在这里,我使用了硬代码来设置颜色,并将自定义渲染器应用于所有内容页面。您可以将其调整为指定的页面并在其中定义可绑定颜色的属性。

或者,我认为您可以利用MessagingCenter来触发工具栏调整代码。

答案 1 :(得分:1)

对我来说,这适用于工具栏:

        var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);

        if (toolbar == null)
            return;

但是,它在OnLayout内部。我目前还不确定,但是很可能您只能在那里找到工具栏。