Gradient view issue on Mac OS Android emulator API level 28 (Pie)

DhaKauDhaKau LKMember ✭✭

Hi All,

I'm trying to create a gradient view in XF using custom renders.

Here is the code for Gradient view in shared project.

`using Xamarin.Forms;

namespace GradientViewDemo
{
public enum GradientOrientation
{
Vertical = 0,
Horizontal = 1
}

public class GradientView : ContentView
{
    public static readonly BindableProperty StartColorProperty = BindableProperty.Create(
        propertyName: nameof(StartColor),
        returnType: typeof(Color),
        declaringType: typeof(GradientView),
        defaultValue: Color.Default);

    public static readonly BindableProperty EndColorProperty = BindableProperty.Create(
        propertyName: nameof(EndColor),
        returnType: typeof(Color),
        declaringType: typeof(GradientView),
        defaultValue: Color.Default);

    public static readonly BindableProperty OrientationProperty = BindableProperty.Create(
        propertyName: nameof(Orientation),
        returnType: typeof(GradientOrientation),
        declaringType: typeof(GradientView),
        defaultValue: GradientOrientation.Vertical);

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

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

    public GradientOrientation Orientation
    {
        get { return (GradientOrientation)GetValue(OrientationProperty); }
        set { SetValue(OrientationProperty, value); }
    }
}

}
`

Android renderer:

`[assembly: ExportRenderer(typeof(GradientView), typeof(GradientViewRenderer))]
namespace GradientViewDemo.Droid
{
public class GradientViewRenderer : ViewRenderer
{
private Xamarin.Forms.Color OldStartColor { get; set; }
private Xamarin.Forms.Color OldEndColor { get; set; }
private Xamarin.Forms.Color StartColor { get; set; }
private Xamarin.Forms.Color EndColor { get; set; }
private GradientOrientation Orientation { get; set; }

    public GradientViewRenderer(Context context) : base(context) { }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        if (e.PropertyName == GradientView.StartColorProperty.PropertyName || e.PropertyName == GradientView.EndColorProperty.PropertyName)
        {
            UpdateColors();
        }

        if (e.PropertyName == GradientView.OrientationProperty.PropertyName)
        {
            UpdateOrientation();
        }
    }

    protected override void OnElementChanged(ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null || Element == null)
        {
            return;
        }

        UpdateColors();
        UpdateOrientation();
    }

    private void UpdateColors()
    {
        if (Element is GradientView element)
        {
            OldStartColor = StartColor;
            OldEndColor = EndColor;

            StartColor = element.StartColor;
            EndColor = element.EndColor;
        }
    }

    private void UpdateOrientation()
    {
        if (Element is GradientView element)
        {
            Orientation = element.Orientation;
        }
    }

    protected override void DispatchDraw(Canvas canvas)
    {
        var gradient = new LinearGradient(
            x0: 0,
            y0: 0,
            x1: Orientation == GradientOrientation.Horizontal ? Width : 0,
            y1: Orientation == GradientOrientation.Vertical ? Height : 0,
            color0: StartColor.ToAndroid(),
            color1: EndColor.ToAndroid(),
            tile: Shader.TileMode.Mirror);
        var paint = new Paint
        {
            Dither = true
        };

        paint.SetShader(gradient);
        canvas.DrawPaint(paint);
        base.DispatchDraw(canvas);
    }
}

}`

XAML page:

`<?xml version="1.0" encoding="utf-8" ?>

<StackLayout>
    <!-- Place new controls here -->
    <StackLayout Spacing="40" Margin="20">
        <Label Text="test.."/>
        <Label Text="test.."/>
        <Button Text="Button 1" 
                Clicked="Handle_Clicked1"/>
        <local:GradientView StartColor = "Red"
                            EndColor = "Yellow"
                            VerticalOptions="FillAndExpand">
            <StackLayout Spacing="40">
                <Label Text="test.."/>
                <BoxView BackgroundColor="Blue" WidthRequest="200" HeightRequest="100"/>
                <Label Text="test.."/>
                <Button Text="Button 2" 
                        Clicked="Handle_Clicked2"/>
            </StackLayout>
        </local:GradientView>

    </StackLayout>
</StackLayout>


`

This code works fine in Android 9 emulator on Windows.

But when we run same code on VS Mac page doesn't rendered as expected. Gradient view spreads all over the view. Acts as a input transparent view where we can click hidden elements(Button 1).

This work on older Mac emulators, issue is only with Android 9.0.

Any ideas?

Thanks
Kaushalya

Best Answer

  • DhaKauDhaKau LK ✭✭
    Accepted Answer

    @richp582 found an alternative approach. Rather than overriding DispatchDraw(Canvas canvas) set the background with
    var orientation = GradientDrawable.Orientation.TopBottom; var gradient = new GradientDrawable(orientation, new[] { StartColor.ToAndroid().ToArgb(), EndColor.ToAndroid().ToArgb() }); ViewHelper.SetBackground(this, gradient);

    Hope this helps :).

    Kaushalya

Answers

  • richp582richp582 USMember ✭✭

    Sorry, I don't have an answer, but I'm seeing this behavior too only in Android 9.0 emulators and physical devices.

    Did you find a solution?

  • DhaKauDhaKau LKMember ✭✭
    Accepted Answer

    @richp582 found an alternative approach. Rather than overriding DispatchDraw(Canvas canvas) set the background with
    var orientation = GradientDrawable.Orientation.TopBottom; var gradient = new GradientDrawable(orientation, new[] { StartColor.ToAndroid().ToArgb(), EndColor.ToAndroid().ToArgb() }); ViewHelper.SetBackground(this, gradient);

    Hope this helps :).

    Kaushalya

  • richp582richp582 USMember ✭✭

    Cool! Thanks, the behavior I saw was a little different than OPs. I had Gradient in a stack panel at the top 1/4 of the screen and on 8.1 and below it worked perfect with NO gradient in the bottom 3/4 but in 9.0 the Gradient repeated through the whole screen. Forcing a white backgroundcolor was a work around for me as it pretty much hid the bug... but I'll try this out later!

    Thanks,
    Rich

  • @DhaKau Thanks for your workaround! I faced with the same issue and your solution works like a charm!

  • DhaKauDhaKau LKMember ✭✭

    @Grigory.4715 Happy to hear that :)

  • @DhaKau said:
    @richp582 found an alternative approach. Rather than overriding DispatchDraw(Canvas canvas) set the background with
    var orientation = GradientDrawable.Orientation.TopBottom; var gradient = new GradientDrawable(orientation, new[] { StartColor.ToAndroid().ToArgb(), EndColor.ToAndroid().ToArgb() }); ViewHelper.SetBackground(this, gradient);

    Hope this helps :).

    Kaushalya

    This Crash on android 4.4
    Did you find a solution?

  • RicardoPORRicardoPOR Member ✭✭

    @DhaKau said:
    @richp582 found an alternative approach. Rather than overriding DispatchDraw(Canvas canvas) set the background with
    var orientation = GradientDrawable.Orientation.TopBottom; var gradient = new GradientDrawable(orientation, new[] { StartColor.ToAndroid().ToArgb(), EndColor.ToAndroid().ToArgb() }); ViewHelper.SetBackground(this, gradient);

    Hope this helps :).

    Kaushalya

    This solution work, but instead of using ViewHelper.SetBackground(this, gradient); i used:
    ViewCompat.SetBackground(this, gradient);

Sign In or Register to comment.