Xamarin

Xamarin.Formsで背景色をグラデーション表示 - NControl編

More than 1 year has passed since last update.

前回はカスタムレンダラーを使用しましたが、やはりXamarin.Formsではできるだけネイティブコードを書きたくないので、カスタムレンダラー無しでやってみたいと思います。

NControl というクロスプラットフォームの2D描画パッケージを使うとグラデーションを描画できるっぽいです(実際の描画は依存パッケージである NGraphics が担当)。

パッケージ追加

ソリューション内の各プロジェクト(PCL/iOS/Android)に、下記のパッケージを追加します。

  • NControl
  • NControl.Controls

nuget.png

初期化

NControl の初期化コードを追加します。

iOS

AppDelegate.cs
using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

using NControl.iOS; // <-- 追加

namespace MyApp.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            NControlViewRenderer.Init();    // <-- 追加

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Android

MainActivity.cs
using System;

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using NControl.Droid;   // <-- 追加

namespace MyApp.Droid
{
    [Activity(Label = "MyApp.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            NControlViewRenderer.Init();    // <-- 追加

            LoadApplication(new App());
        }
    }
}

実装

GradientView.cs
using System;

using Xamarin.Forms;

using NControl.Abstractions;
using NControl.Controls;
using NGraphics;

namespace MyApp
{
    public class GradientView : NControlView
    {
        public GradientView()
        {
            DrawingFunction = (canvas, rect) =>
            {
                var brush = new LinearGradientBrush(
                    NGraphics.Point.Zero,
                    NGraphics.Point.OneY,
                    this.StartColor.ToNColor(),
                    this.EndColor.ToNColor()
                );
                canvas.FillRectangle(rect, brush);
            };
        }

        protected void OnColorChanged()
        {
            this.Invalidate();
        }

        public static readonly BindableProperty StartColorProperty = BindableProperty.Create(
            "StartColor", typeof(Xamarin.Forms.Color), typeof(GradientView), Xamarin.Forms.Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((GradientView)bindable).OnColorChanged()
        );

        public static readonly BindableProperty EndColorProperty = BindableProperty.Create(
            "EndColor", typeof(Xamarin.Forms.Color), typeof(GradientView), Xamarin.Forms.Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((GradientView)bindable).OnColorChanged()
        );

        public Xamarin.Forms.Color StartColor {
            get { return (Xamarin.Forms.Color)GetValue(StartColorProperty); }
            set { SetValue(StartColorProperty, value); }
        }

        public Xamarin.Forms.Color EndColor {
            get { return (Xamarin.Forms.Color)GetValue(EndColorProperty); }
            set { SetValue(EndColorProperty, value); }
        }

    }
}
XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyApp"
    x:Class="MyApp.MyAppPage">

    <local:GradientView StartColor="Olive" EndColor="Teal" />

</ContentPage>

結果

iOS

result_ios.png

Android

result_android.png

できました。
カスタムレンダラーと比べると簡単な感じがしますね。

3色のグラデーション

3色でもグラデーションできます。

GradientView.cs
using System;

using Xamarin.Forms;

using NControl.Abstractions;
using NControl.Controls;
using NGraphics;

namespace MyApp
{
    public class GradientView : NControlView
    {
        public GradientView()
        {
            DrawingFunction = (canvas, rect) =>
            {
                LinearGradientBrush brush;

                if (this.MidColor != Xamarin.Forms.Color.Default) {
                    brush = new LinearGradientBrush(
                        NGraphics.Point.Zero,
                        NGraphics.Point.OneY,
                        this.StartColor.ToNColor(),
                        this.MidColor.ToNColor(),
                        this.EndColor.ToNColor()
                    );
                } else {
                    brush = new LinearGradientBrush(
                        NGraphics.Point.Zero,
                        NGraphics.Point.OneY,
                        this.StartColor.ToNColor(),
                        this.EndColor.ToNColor()
                    );
                }

                canvas.FillRectangle(rect, brush);
            };
        }

        protected void OnColorChanged()
        {
            this.Invalidate();
        }

        public static readonly BindableProperty StartColorProperty = BindableProperty.Create(
            "StartColor", typeof(Xamarin.Forms.Color), typeof(GradientView), Xamarin.Forms.Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((GradientView)bindable).OnColorChanged()
        );

        public static readonly BindableProperty MidColorProperty = BindableProperty.Create(
            "MidColor", typeof(Xamarin.Forms.Color), typeof(GradientView), Xamarin.Forms.Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((GradientView)bindable).OnColorChanged()
        );

        public static readonly BindableProperty EndColorProperty = BindableProperty.Create(
            "EndColor", typeof(Xamarin.Forms.Color), typeof(GradientView), Xamarin.Forms.Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((GradientView)bindable).OnColorChanged()
        );

        public Xamarin.Forms.Color StartColor {
            get { return (Xamarin.Forms.Color)GetValue(StartColorProperty); }
            set { SetValue(StartColorProperty, value); }
        }

        public Xamarin.Forms.Color MidColor {
            get { return (Xamarin.Forms.Color)GetValue(MidColorProperty); }
            set { SetValue(MidColorProperty, value); }
        }

        public Xamarin.Forms.Color EndColor {
            get { return (Xamarin.Forms.Color)GetValue(EndColorProperty); }
            set { SetValue(EndColorProperty, value); }
        }

    }
}
XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyApp"
    x:Class="MyApp.MyAppPage">

    <local:GradientView StartColor="Olive" MidColor="Red" EndColor="Teal" />

</ContentPage>

iOS

result_ios_2.png

Android

result_android_2.png

若干不自然な感じもしなくはないですが、使いようによっては効果があるのではないでしょうか。

カスタムレンダラーを書かなくても、代わりにやってくれる便利なパッケージを使うと簡単に実装できますね。

とりあえずは以上です。