shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.FormsでStackLayoutの背景グラデーション:メモ

qiita.com

forums.xamarin.com

こちらの動画の中のデモアプリケーションでもShellのドロワーっていうのかな,左から出てくるメニューの背景でグラデーションが使われている.

www.youtube.com

背景にグラデーションをつけるにはカスタムレンダラーを使う必要がある.

ここがわかりやすかった. qiita.com

Androidのコードだけ.

手順としては大まかに以下のとおり.

StackLayoutなどのレイアウト系要素について.

  1. 各クラスを継承した新しいクラスを作成する.
  2. グラデーションに使う2色を指定するプロパティ(StartColor,EndColorと名前をつけていることが多い)を追加する.
  3. CustomRendererを作る.

コントロールを継承したクラスを作る.

ついでにBindingできるようにBindablePropertyにする.

    public class GradientStackLayout : StackLayout
    {
        public Color StartColor
        {
            get { return (Color)GetValue(StartColorProperty); }
            set { SetValue(StartColorProperty, value); }
        }
        public static readonly BindableProperty StartColorProperty = BindableProperty.Create(
               propertyName: nameof(StartColor),
               returnType: typeof(Color),
               declaringType: typeof(GradientStackLayout),
               defaultValue: Color.Transparent,
               defaultBindingMode: BindingMode.OneWay,
               propertyChanged: StartColorPropertyChanged
            );
        private static void StartColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            //Do something if you need.
        }

        public Color EndColor
        {
            get { return (Color)GetValue(EndColorProperty); }
            set { SetValue(EndColorProperty, value); }
        }
        public static readonly BindableProperty EndColorProperty = BindableProperty.Create(
                propertyName: nameof(EndColor),
                returnType: typeof(Color),
                declaringType: typeof(GradientStackLayout),
                defaultValue: Color.Transparent,
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: EndColorPropertyChanged
            );
        private static void EndColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            //Do something if you need.
        }

        public float GradientRatio
        {
            get { return (float)GetValue(GradientRatioProperty); }
            set { SetValue(GradientRatioProperty, value); }
        }
        public static readonly BindableProperty GradientRatioProperty = BindableProperty.Create
            (
                propertyName: nameof(GradientRatio),
                returnType: typeof(float),
                declaringType: typeof(GradientStackLayout),
                defaultValue: 1.0f,
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: GradientRatioPropertyChanged
            );
        private static void GradientRatioPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
        }
    }

Custom Rendererを作る

[assembly: ExportRenderer(typeof(GradientStackLayout), typeof(GradientStackLayoutRenderer))]
namespace CustomRenderer.Droid.Renderers
{
    public class GradientStackLayoutRenderer : VisualElementRenderer<GradientStackLayout>
    {
        public GradientStackLayoutRenderer(Context context) : base(context)
        {
        }

        protected override void DispatchDraw(Canvas canvas)
        {
            //X軸始点
            //Y軸始点
            //X軸終点
            //Y軸終点
            //開始色
            //終了色
            //範囲外の描画方法
            LinearGradient gradient = new LinearGradient
                (
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: Height*Element.GradientRatio,
                    color0: Element.StartColor.ToAndroid(),
                    color1: Element.EndColor.ToAndroid(),
                    tile: Shader.TileMode.Clamp
                );

            Paint paint = new Paint
            {
                Dither = true,
            };
            paint.SetShader(gradient);
            canvas.DrawPaint(paint);

            base.DispatchDraw(canvas);
        }
    }
}

大事なことは以下の属性部分.

[assembly: ExportRenderer(typeof(GradientStackLayout), typeof(GradientStackLayoutRenderer))]

1つ目の引数で指定されているコントロールGradientStackLayoutに対して2つ目の引数でしたレンダラーを使ってUIコントロールを描画する,という指定.

f:id:shuhelohelo:20200111080956p:plain

今回のソースコードはこちら

github.com

20200111追記: グラデーションの方向を指定できるようにした.

GradientHorizontalDirectionとGradientVerticalDirectionを追加.