Search code examples
androidxamarinxamarin.formsxamarin.androidrenderer

Custom Frame renderer for unique corner radius values in android [Xamarin.Forms]


I want to create a custom renderer for my frame control in fact of having a control which each corner radius values are unique, I have tried to create a custom renderer for frame control in many ways but I haven't found a "solid" solution for it, my custom renderer is not working, I also have tried to use Xamarin.Forms.PancackeView plugin but in my Xamarin.Forms version (4.3.0.991) it seems to have a bug or something related:

EXTRA: I want to implement shadow in the control frame, but as you should know, frame control by default has shadow, but the shadow in my frame is not working, is there a bug or how can I implement shadow to the custom frame renderer?

Android code for custom Frame renderer:

[assembly: ExportRenderer(typeof(TagFrame), typeof(TagFrameCustomRendererAndroid))]
namespace Sortex.Droid.CRImplementations
{
    public class TagFrameCustomRendererAndroid : FrameRenderer
    {
        public TagFrameCustomRendererAndroid(Context context) : base(context)
        {
        }

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

            if (e.NewElement != null)
            {
                GradientDrawable gradientDrawable = new GradientDrawable();

                float[] cornerRadius = { 100000, 100000, 0, 0, 0, 0, 100000, 100000 };
                gradientDrawable.SetCornerRadii(cornerRadius);

                SetBackgroundDrawable(gradientDrawable);
            }
        }
    }
}

TagFrame class in the shared project:

public class TagFrame : Frame
{
}

Solution

  • I want to create a custom renderer for my frame control in fact of having a control which each corner radius values are unique,

    According to your description, you want to custom render frame, then you can set different corner radius. If yes, I do one sample that you can take a look:

    Firstly, create CustomFrame inherit Frame, has BindableProperty CornerRadiusProperty.

    public class CustomFrame:Frame
    {
        public static new readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CustomFrame), typeof(CornerRadius), typeof(CustomFrame));
    
        public CustomFrame()
        {
            // MK Clearing default values (e.g. on iOS it's 5)
            base.CornerRadius = 0;
        }
    
        public new CornerRadius CornerRadius
        {
            get => (CornerRadius)GetValue(CornerRadiusProperty);
            set => SetValue(CornerRadiusProperty, value);
        }
    }
    

    Then going to Android platform,

    using FrameRenderer = Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer;
    
    
    [assembly: ExportRenderer(typeof(CustomFrame), typeof(CustomFrameRenderer))]
    namespace Framerender.Droid
    {
      public  class CustomFrameRenderer: FrameRenderer
        {
            public CustomFrameRenderer(Context context)
                : base(context)
            {
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement != null && Control != null)
                {
                    UpdateCornerRadius();
                }
            }
    
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged(sender, e);
    
                if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
                    e.PropertyName == nameof(CustomFrame))
                {
                    UpdateCornerRadius();
                }
            }
    
            private void UpdateCornerRadius()
            {
                if (Control.Background is GradientDrawable backgroundGradient)
                {
                    var cornerRadius = (Element as CustomFrame)?.CornerRadius;
                    if (!cornerRadius.HasValue)
                    {
                        return;
                    }
    
                    var topLeftCorner = Context.ToPixels(cornerRadius.Value.TopLeft);
                    var topRightCorner = Context.ToPixels(cornerRadius.Value.TopRight);
                    var bottomLeftCorner = Context.ToPixels(cornerRadius.Value.BottomLeft);
                    var bottomRightCorner = Context.ToPixels(cornerRadius.Value.BottomRight);
    
                    var cornerRadii = new[]
                    {
                        topLeftCorner,
                        topLeftCorner,
    
                        topRightCorner,
                        topRightCorner,
    
                        bottomRightCorner,
                        bottomRightCorner,
    
                        bottomLeftCorner,
                        bottomLeftCorner,
                    };
    
                    backgroundGradient.SetCornerRadii(cornerRadii);
                }
            }
        }
    }
    

    Now, you can use CustomFrame in PCL.

     <local:CustomFrame
            BackgroundColor="Red"
            CornerRadius="0,0,30,30"
            HasShadow="True"
            HeightRequest="100"
            VerticalOptions="CenterAndExpand"
            WidthRequest="100" />
    

    Here is the sample at Github, you can also take a look:

    https://github.com/CherryBu/CustomFrame

    There are also one article about customframe:

    https://progrunning.net/customizing-corner-radius/