Search code examples
xamarinxamarin.androidxamarin.formsportable-class-libraryrenderer

Xamarin android renderer: button is round the first time, but when pressed on the backbutton it becomes square


I have made a renderer to make a round button in Android. It gets executed with no problem when you enter the page normally. But when you press the backbutton to go back to that page the button becomes a square.

My renderer code:

public class FloatingActionButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
    private GradientDrawable _normal, _pressed;

    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            var button = (FloatingActionButton)e.NewElement;

            button.SizeChanged += OnSizeChanged;

        }
    }

    private void OnSizeChanged(object s, EventArgs e) {
        var button = (FloatingActionButton)s;
        var radius = (float)Math.Min(button.Width, button.Height) * Resources.DisplayMetrics.Density;

        // Create a drawable for the button's normal state
        _normal = new Android.Graphics.Drawables.GradientDrawable();

        if (button.BackgroundColor.R == -1.0 && button.BackgroundColor.G == -1.0 && button.BackgroundColor.B == -1.0)
            _normal.SetColor(Android.Graphics.Color.ParseColor("#ff2c2e2f"));
        else
            _normal.SetColor(button.BackgroundColor.ToAndroid());

        _normal.SetCornerRadius(radius);

        // Create a drawable for the button's pressed state
        _pressed = new Android.Graphics.Drawables.GradientDrawable();
        var highlight = Context.ObtainStyledAttributes(new int[] { Android.Resource.Attribute.ColorActivatedHighlight }).GetColor(0, Android.Graphics.Color.Gray);
        _pressed.SetColor(highlight);
        _pressed.SetCornerRadius(radius);

        // Add the drawables to a state list and assign the state list to the button
        var sld = new StateListDrawable();
        sld.AddState(new int[] { Android.Resource.Attribute.StatePressed }, _pressed);
        sld.AddState(new int[] { }, _normal);
        Control.SetBackground(sld);
        button.SizeChanged -= OnSizeChanged;
    }
}

So I create a handler for size changed. It gets created with no problem both times but it only enters the event when you enter the page normally. It doesn't enter when you press the backbutton.

Screenshots of the floating action button: button

Edit Some extra information: I forgot to mention that I override the OnBackButtonPressed of my MasterDetailPage, I did this because otherwise it was crashing when I pressed on the backbutton:

protected override bool OnBackButtonPressed()
{

    Page page = GoPrevPage();
    if (page.GetType() == typeof(LoginPage))
    {
        App.Current.MainPage = new LoginPage();
    }

    else
    {
        page.Parent = null;
        Detail = page;

        navigationDrawerList.SelectedItem = selectedMenuItems.LastOrDefault();

    }
    return true;
}

The GoPrevPage is a function coming for a static class:

public static class BackButtonHelper
{
    public static List<Page> prevPages;
    public static List<MasterPageItem> selectedMenuItems;

    static BackButtonHelper() {
        prevPages = new List<Page>();
        selectedMenuItems = new List<MasterPageItem>();
    }

    public static Page GoPrevPage() {
        prevPages.RemoveAt(prevPages.Count - 1);
        selectedMenuItems.RemoveAt(selectedMenuItems.Count - 1);
        return prevPages[prevPages.Count - 1]; 
    }
    public static void AddPageToPrev(Page page, MasterPageItem masterPageItem)
    {

        if (!IsToegevoegd(page.ClassId))
        {
            prevPages.Add(page);
            selectedMenuItems.Add(masterPageItem);
        }


    }
    private static bool IsToegevoegd(string title) {

        return prevPages.Last().ClassId == title;
    }
}

And this is the action that happens when you click on the action button(the navigation:

private void insertTaak_Clicked(object sender, EventArgs e)
{
    var navPage = new DetailTaak("0") { Title = "Taak toevoegen" };

    var app = Application.Current as App;

    var mainPage = (MenuPage)app.MainPage;


    mainPage.Detail = new NavigationPageBar(navPage);
}

Solution

  • I say forget about the size change event and try the update directly

    public class FloatingActionButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
    {
        private GradientDrawable _normal, _pressed;
    
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
    
            if (Control != null)
            {
                var button = (FloatingActionButton)e.NewElement;
                this.UpdateStyle(button);
            }
        }
    
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == FloatingActionButton.HeightProperty.PropertyName ||
                e.PropertyName == FloatingActionButton.WidthProperty.PropertyName)
            {
                var button = (FloatingActionButton)sender;
                UpdateStyle(button);
            }
            else
            {
                base.OnElementPropertyChanged(sender, e);
            }
        }
    
        private void UpdateStyle(FloatingActionButton button)
        {
            try
            {
                var radius = (float)Math.Min(button.Width, button.Height) * Resources.DisplayMetrics.Density;
    
                // Create a drawable for the button's normal state
                _normal = new Android.Graphics.Drawables.GradientDrawable();
    
                if (button.BackgroundColor.R == -1.0 && button.BackgroundColor.G == -1.0 && button.BackgroundColor.B == -1.0)
                    _normal.SetColor(Android.Graphics.Color.ParseColor("#ff2c2e2f"));
                else
                    _normal.SetColor(button.BackgroundColor.ToAndroid());
    
                _normal.SetCornerRadius(radius);
    
                // Create a drawable for the button's pressed state
                _pressed = new Android.Graphics.Drawables.GradientDrawable();
                var highlight = Context.ObtainStyledAttributes(new int[] { Android.Resource.Attribute.ColorActivatedHighlight }).GetColor(0, Android.Graphics.Color.Gray);
                _pressed.SetColor(highlight);
                _pressed.SetCornerRadius(radius);
    
                // Add the drawables to a state list and assign the state list to the button
                var sld = new StateListDrawable();
                sld.AddState(new int[] { Android.Resource.Attribute.StatePressed }, _pressed);
                sld.AddState(new int[] { }, _normal);
                Control.SetBackground(sld);
            }
            catch
            {
                //...No-op
            }
        }
    }