Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I change the colors of a stepper for iOS and Android?

My code uses a stepper which looks like this:

Stepper control

How can I change the color Blue to Red for the iOS and Android versions of the stepper by setting the new color in XAML?

like image 276
Alan2 Avatar asked Jan 31 '18 08:01

Alan2


3 Answers

This can be done using Effects.

Code

I've created a sample app here: https://github.com/brminnick/CustomStepper

Consuming the Effects 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" 
    x:Class="CustomStepper.StepperPage"
    xmlns:local="clr-namespace:CustomStepper">
    
    <ContentPage.Content>
        <Stepper
            HorizontalOptions="Center"
            VerticalOptions="Center"
            local:StepperColorEffect.Color="Red"/>
    </ContentPage.Content>
</ContentPage>

Stepper Color Effect

using System.Linq;

using Xamarin.Forms;

namespace CustomStepper
{
    public static class StepperColorEffect
    {
        public static readonly BindableProperty ColorProperty =
            BindableProperty.CreateAttached(nameof(Color),
                typeof(Color),
                typeof(Stepper),
                Color.Gray,
                propertyChanged: OnStepperColorChanged);

        public static Color GetColor(BindableObject view) => (Color)view.GetValue(ColorProperty);

        public static void SetColor(BindableObject view, Color value) => view.SetValue(ColorProperty, value);

        static void OnStepperColorChanged(BindableObject bindable, object oldValue, object newValue) => UpdateEffect(bindable);

        static void UpdateEffect(BindableObject bindable)
        {
            var stepper = (Stepper)bindable:
            RemoveEffect(stepper);
            stepper.Effects.Add(new StepperColorRoutingEffect());
        }

        static void RemoveEffect(Stepper entry)
        {
            var effectToRemoveList = entry.Effects.OfType<StepperColorRoutingEffect>();

            foreach (var entryReturnTypeEffect in effectToRemoveList)
                entry.Effects.Remove(entryReturnTypeEffect);
        }
    }

    class StepperColorRoutingEffect : RoutingEffect
    {
        public StepperColorRoutingEffect() : base("CustomStepper.StepperColorEffect")
        {
        }
    }
}

iOS PlatformEffect

using UIKit;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using CustomStepper.iOS;

[assembly: ResolutionGroupName("CustomStepper")]
[assembly: ExportEffect(typeof(StepperColorEffect), nameof(StepperColorEffect))]
namespace CustomStepper.iOS
{
    public class StepperColorEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            if (Element is Stepper element && Control is UIStepper control)
            {
                control.TintColor = CustomStepper.StepperColorEffect.GetColor(element).ToUIColor();
                control.SetIncrementImage(Control.GetIncrementImage(UIControlState.Normal), UIControlState.Normal);
                control.SetDecrementImage(Control.GetDecrementImage(UIControlState.Normal), UIControlState.Normal);
            }
        }

        protected override void OnDetached()
        {
            if (Element is Stepper element && Control is UIStepper control)
            {
                control.TintColor = UIColor.Blue;
                control.SetIncrementImage(Control.GetIncrementImage(UIControlState.Normal), UIControlState.Normal);
                control.SetDecrementImage(Control.GetDecrementImage(UIControlState.Normal), UIControlState.Normal);
            }
        }
    }
}

Android PlatformEffect

using Android.Widget;
using Android.Graphics;

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

using CustomStepper.Droid;

[assembly: ResolutionGroupName("CustomStepper")]
[assembly: ExportEffect(typeof(StepperColorEffect), nameof(StepperColorEffect))]
namespace CustomStepper.Droid
{
    public class StepperColorEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            if (Element is Stepper element && Control is LinearLayout control)
            {
                control.GetChildAt(0).Background.SetColorFilter(CustomStepper.StepperColorEffect.GetColor(element).ToAndroid(), PorterDuff.Mode.Multiply);
                control.GetChildAt(1).Background.SetColorFilter(CustomStepper.StepperColorEffect.GetColor(element).ToAndroid(), PorterDuff.Mode.Multiply);
            }
        }

        protected override void OnDetached()
        {
            if (Element is Stepper element && Control is LinearLayout control)
            {
                control.GetChildAt(0).Background.SetColorFilter(Xamarin.Forms.Color.Gray.ToAndroid(), PorterDuff.Mode.Multiply);
                control.GetChildAt(1).Background.SetColorFilter(Xamarin.Forms.Color.Gray.ToAndroid(), PorterDuff.Mode.Multiply);
            }
        }
    }
}

Screenshots

Android

enter image description here

iOS

enter image description here

like image 108
Brandon Minnick Avatar answered Oct 26 '22 06:10

Brandon Minnick


How can I change the colors of a stepper for iOS and Android?

In your Xamarin.Android project, you could create a custom vector drawable and use it as a background to achieve the same appearance as the picture you have post above.

Place the button_selector.xml and button_border.xml file in your Android Resources\drawable folder:

button_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/colorAccent" android:state_pressed="true"/>
  <item android:drawable="@color/colorPrimaryDark" android:state_focused="true"/>
  <item android:drawable="@drawable/button_border"/>
</selector>

button_border.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle" >

  <solid android:color="#00FFFFFF" />
  <corners android:radius="5dp" />
  <stroke
    android:width="2dp"
    android:color="#FFFFFF" />

</shape>

In your ExtStepperRenderer:

protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
    base.OnElementChanged(e);
    MyStepper s = Element as MyStepper;

    if (Control != null)
    {
        var button = Control.GetChildAt(0) as Android.Widget.Button;
        button.SetTextColor(s.MyColor.ToAndroid());
        button.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Drawable.button_selector, null));
        button.Background.SetColorFilter(s.MyColor.ToAndroid(), PorterDuff.Mode.Multiply);

        var button2 = Control.GetChildAt(1) as Android.Widget.Button;
        button2.SetTextColor(s.MyColor.ToAndroid());
        button2.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Drawable.button_selector, null));
        button2.Background.SetColorFilter(s.MyColor.ToAndroid(), PorterDuff.Mode.Multiply);
    }
}

Effect on Android device:

enter image description here

like image 29
York Shen Avatar answered Oct 26 '22 06:10

York Shen


This is my solution that also refer from @Alan2's answer.

Xamarin.Forms

    public class StepperExtend : Stepper
{
    public static readonly BindableProperty ColorProperty =
        BindableProperty.Create(
            nameof(Color),
            typeof(Color),
            typeof(StepperExtend),
            Color.Default);

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

Xamarin.Android

[assembly:ExportRenderer(typeof(StepperExtend), typeof(StepperExtendRenderer))]

namespace HydroUkPoc.Frontend.Forms.Droid.Renderer { public class StepperExtendRenderer : StepperRenderer { StepperExtend FormElement { get { return Element as StepperExtend; } }

    protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
    {
        base.OnElementChanged(e);
        if (Control != null)
        {
            UpdateColor();
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == StepperExtend.ColorProperty.PropertyName)
        {
            UpdateColor();
        }
        else
        {
            base.OnElementPropertyChanged(sender, e);
        }
    }

    private void UpdateColor()
    {
        Control.GetChildAt(0).Background.SetColorFilter(FormElement.Color.ToAndroid(), PorterDuff.Mode.Multiply);
        Control.GetChildAt(1).Background.SetColorFilter(FormElement.Color.ToAndroid(), PorterDuff.Mode.Multiply);
    }
}

}

Xamarin.iOS

[assembly:ExportRenderer(typeof(StepperExtend), typeof(StepperExtendRenderer))]

namespace HydroUkPoc.Frontend.Forms.iOS.Renderer { public class StepperExtendRenderer : StepperRenderer { StepperExtend FormElement { get { return Element as StepperExtend; } }

    protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
    {
        base.OnElementChanged(e);
        if (Control != null)
        {
            UpdateColor();
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == StepperExtend.ColorProperty.PropertyName)
        {
            UpdateColor();
        }
        else
        {
            base.OnElementPropertyChanged(sender, e);
        }
    }

    private void UpdateColor()
    {
        Control.TintColor = FormElement.Color.ToUIColor();
    }
}

}

like image 37
Tony Pham Avatar answered Oct 26 '22 07:10

Tony Pham