Gradient as background color

ASIMOASIMO ESMember

I want to set a gradient background to a Xamarin.Forms layout and view. How can i do that?

A tricky but not very appealing is using a WebView and settings the gradient background using CSS but i want to avoid this.

I already searched the forum but didn't found anything, sorry if i missed something.

«1

Posts

  • CraigDunnCraigDunn USXamarin Team Xamurai
    edited August 2014

    There's no real drawing API currently in Xamarin.Forms that would let you render a gradient in a platform-independent way. A couple of options might be:

    • an image of the gradient set to fill the entire screen. you could supply multiple resolution images on each platform to avoid too much pixellation

    • a custom renderer on each platform that uses native APIs to draw the gradient on each platform

  • ASIMOASIMO ESMember

    Thanks very much @CraigDunn‌. I will take a look tu costom renderer.

  • DanBDanB DEMember

    @ASIMO‌

    Hi, did you found a solution for this problem ? i am struggling with the same issue.

    many thx.

  • DanBDanB DEMember

    Robert, thanks for your Input and help ! This is a little different approach that I have running now for WP and iOS. But maybe yours is better.

    I will have a look at it the next days. Still looking for Code for android…

  • Greg.8674Greg.8674 USMember ✭✭

    Any way to do this for a Button ? I wish not to use an image as my app's button gradient colors will vary.
    I have attempted to use a ButtonRenderer class but cant seem to access a:

    myButton.Layer.InsertSublayer (gradientLayer, 0);

    thanks.

  • VictorHGarciaVictorHGarcia USUniversity ✭✭

    I was wondering, I know this is not CSS, but is possible to put a 2x2 pixels background image and repeat-y or repeat-x ?

    just curiosity

  • Beachside_JasonBeachside_Jason USMember ✭✭

    @robermiles3 Your solution works! Any idea how to get it working when using XAML? It looks like XAML auto-generates the base class to be 'ContentPage' and I don't see an option to switch it to 'GradientContentPage'. Thanks in advance. Cheers!

  • powerdudepowerdude USMember ✭✭

    @danb, can you share your approach for WP?

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    @Beachside_Jason just change the page class in the XAML and the code-behind file to be GradientContentPage.

  • leye0leye0 USMember ✭✭

    CraigDunn: Wouldn't filling the entire screen with an image crash the application with a memory leak?

  • DanBDanB DEMember

    @powerdude:

    i am Setting the gradient for the Background in the native WP Project -> App.xaml.cs

    After
    RootFrame = new PhoneApplicationFrame();

    i am calling my own method:

    private void SetBackground()
    {
    
      LinearGradientBrush linear = new LinearGradientBrush();
      linear.StartPoint = new Point(0.5, 0);
      linear.EndPoint = new Point(0.5, 1);
    
      linear.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(0x9C, 0x0C, 0x27, 0x59), Offset = 0.0 });
      linear.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(0xFF, 0xA0, 0x5A, 0x12), Offset = 0.5 });
      linear.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(0x9C, 0x18, 0x15, 0x13), Offset = 1.0 });
    
      ScaleTransform myScaleTransform = new ScaleTransform();
      myScaleTransform.CenterX = 0.5;
      myScaleTransform.CenterY = 0.5;
      myScaleTransform.ScaleY = 3.35;
    
      TransformGroup myTransformGroup = new TransformGroup();
      myTransformGroup.Children.Add(myScaleTransform);
    
      linear.RelativeTransform = myTransformGroup;
      RootFrame.Background = linear;
    
    }
    

    https://forums.xamarin.com/utility/thumbnail/7510/cf://uploads/FileUpload/3e/376eb8c1a59b146df48e1b158d7a43.png

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    @TommyBaggett thanks very much for not only posting the NuGet package but also the code so we can all learn. This is superb work and much appreciated.

  • TommyBaggettTommyBaggett USUniversity ✭✭✭

    You're totally welcomed, @DavidDancy! I hope everyone finds it useful. I enjoyed doing it, at least until the Android AppCompat stuff nearly killed me, haha. #tooManyAPILevels

  • @TommyBaggett , you saved my day

  • TommyBaggettTommyBaggett USUniversity ✭✭✭

    That's awesome, @ChristianSulzberger.1085! I'm happy to hear it helped you out, and appreciate you letting me know.

  • stvansolanostvansolano UMInsider, University ✭✭✭
    edited December 2016
    Nice work @TommyBaggett! Have you considered to make it a proposal for [Xamarin Forms Evolution thread](https://forums.xamarin.com/categories/xamarin-forms-evolution/) ? CC @TheRealJasonSmith
  • stvansolanostvansolano UMInsider, University ✭✭✭
    edited December 2016
    That looks great @TommyBaggett! Have you consider to send it as a proposal for XF Evolution forum? https://forums.xamarin.com/categories/xamarin-forms-evolution ? cc @TheRealJasonSmith
  • SagarPanwalaSagarPanwala USMember ✭✭✭

    Hey Guys,
    It almost two years after question raised, is there any resolution for this? I just want to make a view with Gradient.

  • PaulMouraPaulMoura USUniversity ✭✭

    @TommyBaggett I can't seem to get the gradient to display. I've tried adding it both ways. Tried adding to Page, Button, Entry, and Grid. I added the nuget package to both the Android project and the PCL project, just in case. No dice.

  • TommyBaggettTommyBaggett USUniversity ✭✭✭

    Hi @PaulMoura, sorry to hear of the problems. Did you also call the XFGloss init function in your MainActivity's OnCreate method? Also, the BackgroundGradient property is only supported on the ContentPage class and the various *Cell classes.

    You can check out some XAML code that adds background gradients to both the page and individual table/list cells at https://github.com/tbaggett/xfgloss/blob/master/XFGlossSample/Examples/Views/Xaml/BackgroundGradientPage.xaml.

    That code is part of the sample app included in the source, which demonstrates all the properties in use, and is the source of the screen shots in the docs at http://github.com/tbaggett/xfgloss.

    Hope that helps!

  • TommyBaggettTommyBaggett USUniversity ✭✭✭

    @stvansolano said:
    Nice work @TommyBaggett! Have you considered to make it a proposal for Xamarin Forms Evolution thread ? CC @TheRealJasonSmith

    Hey @stvansolano, apologies for missing your earlier comments, and thanks for the kind words! I haven't proposed any of the properties be added because I only support the iOS and Android platforms. Someone else would need to take care of the other platforms. So far, I haven't had any takers for that. :)

  • DaveAuDaveAu AUMember ✭✭

    @TommyBaggett thanks!

  • CostasAletrariCostasAletrari USMember ✭✭

    Execellet @TommyBaggett

  • JasonWang.1467JasonWang.1467 CNMember
    edited June 2017

    Thanks @Vaka.GopiNadhReddy
    After tried different ways, I feel this is the best solution that can be applied to all controls, pages and layouts because StackLayout can be background of almost everything. If can't be, just replace StackLayout and render to the one you need.

    It is not harder than NuGet component: add iOS part into iOS project, Android part into Android project, the class into the form shared/pcl project.
    I used it in Xaml:
    1. add namespace
    xmlns:local="clr-namespace:SharedProject.Views;assembly=ShareProject"

    1. then replace StackLayout with it:
        <local:GradientStack Grid.Row="1" StartColor="#00000000" EndColor="#A0000000" Padding="12, 12, 12, 0" VerticalOptions="End">
         <Labe ....>
        </local:GradientStack>
    

    I also adjusted code in iOS and Android to get different Gradient effect.
    we could add parameters like GradientFromPoint and GradientToPoint.

  • Simon.5621Simon.5621 USMember ✭✭
    edited June 2017

    Using SkiaSharp seems the best way. Just install the SkiaSharp Nuget, add this class to your project und use it in your xaml:

    public partial class GradientView : ContentView
    {
        public Color StartColor { get; set; } = Color.Transparent;
        public Color EndColor { get; set; } = Color.Transparent;
        public bool Horizontal { get; set; } = false;
    
        public GradientView()
        {
            InitializeComponent();
    
            SKCanvasView canvasView = new SKCanvasView();
            canvasView.PaintSurface += OnCanvasViewPaintSurface;
            Content = canvasView;
        }
    
        void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
        {
            SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
    
            canvas.Clear();
    
            var colors = new SKColor[] { StartColor.ToSKColor(), EndColor.ToSKColor()};
            SKPoint startPoint = new SKPoint(0,0);
            SKPoint endPoint = Horizontal ? new SKPoint(info.Width, 0) : new SKPoint(0, info.Height);
    
            var shader = SKShader.CreateLinearGradient(startPoint, endPoint, colors, null, SKShaderTileMode.Clamp);
    
            SKPaint paint = new SKPaint
            {
                Style = SKPaintStyle.Fill,
                Shader = shader
            };
    
            canvas.DrawRect(new SKRect(0, 0, info.Width, info.Height), paint);
        }
    }
    
  • Vaka.GopiNadhReddyVaka.GopiNadhReddy USMember ✭✭✭

    @JasonWang.1467

    Thank you

  • VenkataSwamyVenkataSwamy INMember ✭✭✭
    edited June 2017

    Hi everyone,

    In below code we can set Horizontal and Vertical Gradient(I put it in #region) for any Layout here I write for StackLayout if you want to write another Layout just replace your Layout on StackLayout.

    In PCL:

    GradientColorStack.cs

     using System;
     using Xamarin.Forms;
     namespace GradientColor
       {
         public class GradientColorStack : StackLayout
         {
           public Color StartColor { get; set; }
           public Color EndColor { get; set; }
         } 
      }
    

    Xamarin.Android:

    GradientColorStackRenderer.cs

    using System;
    using GradientColor;
    using GradientColor.Droid;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    [assembly: ExportRenderer(typeof(GradientColorStack), typeof(GradientColorStackRenderer))]
    
    namespace GradientColor.Droid
    {
       public class GradientColorStackRenderer : VisualElementRenderer<StackLayout>
        {
          private Color StartColor { get; set; }
          private Color EndColor { get; set; }
    
      protected override void DispatchDraw(global::Android.Graphics.Canvas canvas)
        {
        #region for Vertical Gradient
        //var gradient = new Android.Graphics.LinearGradient(0, 0, 0, Height,
        #endregion
    
        #region for Horizontal Gradient
          var gradient = new Android.Graphics.LinearGradient(0, 0, Width, 0,
          #endregion
    
            this.StartColor.ToAndroid(),
            this.EndColor.ToAndroid(),
            Android.Graphics.Shader.TileMode.Mirror);
    
        var paint = new Android.Graphics.Paint()
        {
            Dither = true,
        };
        paint.SetShader(gradient);
        canvas.DrawPaint(paint);
        base.DispatchDraw(canvas);
     }
    
      protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
       {
           base.OnElementChanged(e);
    
           if (e.OldElement != null || Element == null)
          {
              return;
          }
        try
        {
            var stack = e.NewElement as GradientColorStack;
            this.StartColor = stack.StartColor;
            this.EndColor = stack.EndColor;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(@"ERROR:", ex.Message);
         }
        }
      }
    

    }

    Xamarin.iOS:

    GradientColorStackRenderer.cs

      using System;
      using CoreAnimation;
      using CoreGraphics;
     using GradientColor;
     using GradientColor.iOS;
     using Xamarin.Forms;
     using Xamarin.Forms.Platform.iOS;
     [assembly: ExportRenderer(typeof(GradientColorStack), typeof(GradientColorStackRenderer))]
    
     namespace GradientColor.iOS
       {
       public class GradientColorStackRenderer : VisualElementRenderer<StackLayout>
          {
         public override void Draw(CGRect rect)
          {
        base.Draw(rect);
        GradientColorStack stack = (GradientColorStack)this.Element;
        CGColor startColor = stack.StartColor.ToCGColor();
    
        CGColor endColor = stack.EndColor.ToCGColor();
    
        #region for Vertical Gradient
    
        var gradientLayer = new CAGradientLayer();
    
        #endregion
    
        #region for Horizontal Gradient
    
        //var gradientLayer = new CAGradientLayer()
        //{
        //  StartPoint = new CGPoint(0, 0.5),
        //  EndPoint = new CGPoint(1, 0.5)
        //};
    
        #endregion
    
        gradientLayer.Frame = rect;
        gradientLayer.Colors = new CGColor[] { startColor, endColor 
        };
    
        NativeView.Layer.InsertSublayer(gradientLayer, 0);
         }
       }
    }
    

    In 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:GradientColor; assembly:GradientColor" x:Class="GradientColor.GradientColorPage">
    <Grid VerticalOptions="FillAndExpand" RowSpacing="0">
      <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid Grid.Row="0" BackgroundColor="Olive">
         <StackLayout VerticalOptions="Center">
            <Label Text="Normal color for stacklayout" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"  TextColor="White"/>
         </StackLayout>
    </Grid>
    <Grid Grid.Row="1">
         <local:GradientColorStack StartColor="Green" EndColor="Red">
               <StackLayout VerticalOptions="CenterAndExpand">
               <Label Text="Gradient color for StackLayout" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"   TextColor="White"/>
               </StackLayout>
    
         </local:GradientColorStack>
       </Grid>
     </Grid>
    </ContentPage>
    

    Here I did small sample

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭
  • mattleibowmattleibow ZAXamarin Team Xamurai

    @Simon.5621 as much as I am a fan of anyone that uses SkiaSharp, you may need to evaluate if the cost of the amount of memory used is worth it. Basically, a bitmap the size of the screen is allocated.

  • fabiorfabior USMember ✭✭

    @mattleibow What would be a better approach to draw gradient in your opinion?
    Is it much different than creating a renderer on each platform and use Android.Graphics.LinearGradient \ CAGradientLayer?
    How does SkiaSharp works? Doesn't it use these native tools too?

    Thanks!

  • mattleibowmattleibow ZAXamarin Team Xamurai

    @fabior, SkiaSharp has it's own rendering engine and everything, this is how it gets the same result on any platform. I then blit that image onto the native view.

    As a result, the SkiaSharp views each have a bitmap to which they draw. They reuse this bitmap for each draw, so this is usually OK. However, including SkiaSharp (~4.5MB per platform architecture), and then allocate a large bitmap, just to draw a gradient is a bit overkill.

    The native renderers in this post are fairly small chunks of code, and they don't add any/much overhead in terms of package size and memory usage. If you just want a gradient background, this is probably a better way to do it.

    Basically: a little more code (native) vs larger package and memory usage (SkiaSharp).

  • fabiorfabior USMember ✭✭

    @mattleibow Thanks! this helps.
    I understand now what you mean.

    So, in other words, the approach is different than Frank's https://github.com/praeclarum/NGraphics correct?
    If I understand correctly, his library calls primitive drawing API right on a view, while SkiaSharp draws on a bitmap in memory and blits it to the view.

    Is both Skia and SkiaSharp both blitting or just SkiaSharp?

    I'm trying to figure out the best library for the job.
    Thanks!

  • fabiorfabior USMember ✭✭

    @mattleibow Also, please excuse my ignorance, but why doesn't Skia\SkiaSharp just call native primitive instead of blitting the image? It might be slower sometimes, depending on what you're drawing, but I think giving the developer the option to cache or not to a bitmap would be better.
    Again, sorry I don't know much Skia\SkiaSharp right now...

  • fabiorfabior USMember ✭✭

    @mattleibow what I meant is, the ideal I think would be a cross platform library which calls primitive drawing API right on the view, but also gives me a way to cache what I am drawing.
    On the other hand, I think the OS has already some optimization in place. I am not sure, but I think under the hood, the OS is caching a bitmap and invalidates it only when something changes.

  • mattleibowmattleibow ZAXamarin Team Xamurai

    @fabior SkiaSharp is different from NGraphics as you mentioned: SkiaSharp is a binding for a C library (https://skia.org/), NGraphics uses the native platform APIs.

    Both have their advantages and disadvantages. SkiaSharp ships its own engine, so It produces a slightly larger (~4MB) package, NGraphics doesn't really add much. However, NGraphics has a much, much smaller set of features.

    I was trying to create to create some cross-platform API as view that could be used, but there are some issues already. A linear gradient is mostly OK, but the radial gradient is a bit dodgy. iOS does most things, but Android does not support ellipse radial gradients, not does it handle the focus point at all. UWP on the other hand cannot do radial gradients at all and we will have to pull in SharpDX to do it.

    I am beginning to think that instead of battling with all these platform differences, I would just go with SkiaSharp... unless you have a simple linear gradient. It all depends on what you are trying to do. If you want a view with a basic gradient, you might be better off just going with NGraphics as this doesn't really add that much overhead. However, if you are going to be doing more complex drawing, or need the images to look exactly the same on all platforms. (That is another difference: NGraphics produces very similar images, but due to platform differences, they aren't exactly the same)

    SkiaSharp is no small kid, here are the APIs and you can do whatever Chrome or Android can do. (Not just the bits in Java, but the actual OS)

    If you want a quick look at what SkiaSharp can do, 100% the same on all platforms (both the code and the output), check out this video, these docs. You can play around with SkiaSharp with workbooks as well.

  • fabiorfabior USMember ✭✭

    @mattleibow said:
    but Android does not support ellipse radial gradients

    Not sure, shouldn't this work?
    https://developer.android.com/reference/android/graphics/RadialGradient.html

    @mattleibow said:
    (That is another difference: NGraphics produces very similar images, but due to platform differences, they aren't exactly the same)

    Interesting. Do you have an example of this or the native APIs which should render same output but they don't ?

Sign In or Register to comment.