Rating Bar

We belong to an era where people are dependent on feedbacks and review from fellow users to make up their minds and decide whether to invest in a certain service or to purchase an article. A rating system always comes in handy in such requirements. So we present you with a precise procedure to create a rating bar in Xamarin for both Android and iOS.

A Rating bar is used to take an input on a numerical scale from the user. It has a vast implementation for survey and feedback requirements. Here, we follow a custom control approach in order to create a rating bar. We start by creating a control for rating bar in the shared project. We implement that control using renderers in both iOS and Android.
Step 1- Create an empty project in Xamarin.Forms, let’s say “RatingBarDemo”.
 
 
 
Step 2- Make a folder named Controls and create a file named CustomRatingBar.cs, In CustomRatingBar, make some bindable property as in the following code:
namespace RatingBarDemo.Controls
{
    class CustomRatingBar:View
    {
        public event EventHandler<float> RatingChanged;
        public event EventHandler Tapped;

        public static BindableProperty IsSmallStyleProperty = BindableProperty.Create("IsSmallStyle", typeof(bool), 
typeof(CustomRatingBar), false);
        public bool IsSmallStyle
        {
            get { return (bool)GetValue(IsSmallStyleProperty); }
            set { SetValue(IsSmallStyleProperty, value); }
        }

        public static BindableProperty IsReadonlyProperty = BindableProperty.Create("IsReadonly", typeof(bool), typeof(CustomRatingBar), true);
        public bool IsReadonly
        {
            get { return (bool)GetValue(IsReadonlyProperty); }
            set { SetValue(IsReadonlyProperty, value); }
        }

        public static BindableProperty MaxStarsProperty = BindableProperty.Create("MaxStars", typeof(int), typeof(CustomRatingBar), 5);
        public int MaxStars
        {
            get { return (int)GetValue(MaxStarsProperty); }
            set { SetValue(MaxStarsProperty, value); }
        }

        public static BindableProperty RatingProperty = BindableProperty.Create("Rating", typeof(float), typeof(CustomRatingBar), 0f);
        public float Rating
        {
            get { return (float)GetValue(RatingProperty); }
            set { SetValue(RatingProperty, value); OnPropertyChanged("Rating"); }
        }

        public static BindableProperty StepSizeProperty = BindableProperty.Create("StepSize", typeof(float), typeof(CustomRatingBar), 0.5f);
        public float StepSize
        {
            get { return (float)GetValue(StepSizeProperty); }
            set { SetValue(StepSizeProperty, value); }
        }

        public void OnRatingChanged(float rating)
        {
            if (RatingChanged != null)
                RatingChanged.Invoke(this, rating);
        }

        public Color GetFillColor()
        {
            return Rating == 0 ? Color.Gray : Rating <= 2 ? Colors.StarRed : Rating <= 3 ? Colors.StarOrange : Rating <= 4 ? Colors.StarYellow : Colors.StarGreen;
        }

        public void OnTapped()
        {
            if (Tapped != null)
                Tapped.Invoke(this, null);
        }
    }
}

Step 3(optional)- Make a Helper class for different colour requisites, below code use.
namespace RatingBarDemo.Helpers
{
    class Colors
    {
        public static readonly Color StarRed = Color.FromHex("#E41E26");
        public static readonly Color StarOrange = Color.FromHex("#FF6817");
        public static readonly Color StarYellow = Color.FromHex("#FFDE17");
        public static readonly Color StarGreen = Color.FromHex("#3AB54A");
    }
}

Step 4 - For Android- Now we make a renderer for rating bar in android.
[assembly: ExportRenderer(typeof(CustomRatingBar), typeof(RatingBarRenderer))]
namespace RatingBarDemo.Droid.Renderers
{
    class RatingBarRenderer:ViewRenderer<CustomRatingBar, RatingBar>
    {
        CustomRatingBar element;
        RatingBar ratingBar;

        protected override void OnElementChanged(ElementChangedEventArgs<CustomRatingBar> e)
        {
            if (e.NewElement == null)
                return;
            element = Element as CustomRatingBar;
            base.OnElementChanged(e);
            if (e.NewElement == null || Control != null)
                return;
            SetStars();
        }

        public void SetStars()
        {
            ratingBar = new RatingBar(Forms.Context, null, element.IsSmallStyle ? Android.Resource.Attribute.RatingBarStyleSmall
                           : Android.Resource.Attribute.RatingBarStyle)
            {
                StepSize = 1.0f,
                NumStars = element.MaxStars,
                Rating = element.Rating,
            };

            RegisterEvents(element.IsReadonly);

            LayerDrawable drawable = (LayerDrawable)ratingBar.ProgressDrawable;
            SetDefaultColor(drawable);
            SetFillColor(drawable);

            SetNativeControl(ratingBar);
        }

        private void RegisterEvents(bool isReadOnly)
        {
            ratingBar.RatingBarChange -= Control_RatingBarChange;
            if (!isReadOnly)
            {
                ratingBar.RatingBarChange += Control_RatingBarChange;
            }
        }

        private void SetFillColor(LayerDrawable drawable)
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
            {
                Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTint(drawable, element.GetFillColor().ToAndroid());
            }
            else
            {
                drawable.GetDrawable(2).SetColorFilter(element.GetFillColor().ToAndroid(),
                 Android.Graphics.PorterDuff.Mode.SrcAtop);
            }
        }

        private void SetDefaultColor(LayerDrawable drawable)
        {
            drawable.GetDrawable(0).SetColorFilter(Android.Graphics.Color.Gray,
                    Android.Graphics.PorterDuff.Mode.SrcAtop); 
        }


        private void Control_RatingBarChange(object sender, RatingBar.RatingBarChangeEventArgs e)
        {
            element.OnRatingChanged(e.Rating);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals(CustomRatingBar.RatingProperty.PropertyName))
            {
                RegisterEvents(false);
                ratingBar.Rating = element.Rating;
                RegisterEvents(element.IsReadonly);
                SetFillColor((LayerDrawable)ratingBar.ProgressDrawable);
            }
        }
    }
}

Step 5 - For Android- Now creating a renderer for iOS for rating bar as well.
[assembly: ExportRenderer(typeof(CustomRatingBar), typeof(RatingBarRenderer))]

namespace RatingBarDemo.iOS.Renderers
{
    class RatingBarRenderer:ViewRenderer<CustomRatingBar, UIView>
    {
        const string IconStarBlank = "ic_star_blank.png";
        const string IconStarYellow = "ic_star_yellow.png";
        const string IconStarGreen = "ic_star_green.png";
        const string IconStarOrange = "ic_star_orange.png";
        const string IconStarRed = "ic_star_red.png";
 
        UIView rateView;
        UIButton oneStar, twoStar, threeStar, fourStar, fiveStar;
        float starSize;
        CustomRatingBar element;


        protected override void OnElementChanged(ElementChangedEventArgs<CustomRatingBar> e)
        {
            base.OnElementChanged(e);
            element = Element as CustomRatingBar;
            if (element == null)
                return;
            InitializeButton();
            SetTouchEvents(element.IsReadonly);

            rateView = new UIView()
            {
                
            };
            if (element.IsSmallStyle)
            {
                starSize = 15;
                rateView.Frame = new CGRect(0, 0, 95, 15);
                SetRatingBarSmall();
                AddStarInView();
                if (element.Rating >= 0)
                    ShowRatingBar();
            }
            else
            {
                starSize = 30;
                rateView.Frame = new CGRect(0, 0, 170, 30);
                SetRatingBarDefault();
                AddStarInView();
                ShowRatingBar();
            }
            SetNativeControl(rateView);
            var tapGesture = new UITapGestureRecognizer(OnRateViewTapped);
            rateView.AddGestureRecognizer(tapGesture);
        }

        private void OnRateViewTapped()
        {
            element.OnTapped();
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals(CustomRatingBar.RatingProperty.PropertyName))
            {
                ShowRatingBar();
            }
            else if (e.PropertyName.Equals(CustomRatingBar.IsReadonlyProperty.PropertyName))
            {
                SetTouchEvents(element.IsReadonly);
            }
        }

        // rating bar bind with value;
        private void ShowRatingBar()
        {
            if (Element.Rating <= 0)
                SetBlankStarRating();
            else if (Element.Rating <= 1)
                SetOneStarRating();
            else if (Element.Rating <= 2)
                SetTwoStarRating();
            else if (Element.Rating <= 3)
                SetThreeStarRating();
            else if (Element.Rating <= 4)
                SetFourStarRating();
            else if (Element.Rating <= 5)
                SetFiveStarRating();
        }

        // button initialize
        private void InitializeButton()
        {
            oneStar = UIButton.FromType(UIButtonType.Custom);
            oneStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);

            twoStar = UIButton.FromType(UIButtonType.Custom);
            twoStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);

            threeStar = UIButton.FromType(UIButtonType.Custom);
            threeStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);

            fourStar = UIButton.FromType(UIButtonType.Custom);
            fourStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);

            fiveStar = UIButton.FromType(UIButtonType.Custom);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }

        void OneStar_TouchUpInside(object sender, EventArgs e)
        {
            element.OnRatingChanged(1);
        }
        void TwoStar_TouchUpInside(object sender, EventArgs e)
        {
            element.OnRatingChanged(2);
        }
        void ThreeStar_TouchUpInside(object sender, EventArgs e)
        {
            element.OnRatingChanged(3);
        }
        void FourStar_TouchUpInside(object sender, EventArgs e)
        {
            element.OnRatingChanged(4);
        }
        void FiveStar_TouchUpInside(object sender, EventArgs e)
        {
            element.OnRatingChanged(5);
        }

        private void SetTouchEvents(bool isReadOnly)
        {
            oneStar.TouchUpInside -= OneStar_TouchUpInside;
            twoStar.TouchUpInside -= TwoStar_TouchUpInside;
            threeStar.TouchUpInside -= ThreeStar_TouchUpInside;
            fourStar.TouchUpInside -= FourStar_TouchUpInside;
            fiveStar.TouchUpInside -= FiveStar_TouchUpInside;
            if (isReadOnly == false)
            {
                oneStar.TouchUpInside += OneStar_TouchUpInside;
                twoStar.TouchUpInside += TwoStar_TouchUpInside;
                threeStar.TouchUpInside += ThreeStar_TouchUpInside;
                fourStar.TouchUpInside += FourStar_TouchUpInside;
                fiveStar.TouchUpInside += FiveStar_TouchUpInside;
            }
        }


        // rating bar size
        private void SetRatingBarSmall()
        {
            //int x = (int)(App.ScreenSize.Width - ratingBarWidth) / 2;
            int x = 0;
            oneStar.Frame = new CGRect(x, 0, starSize, starSize);
            twoStar.Frame = new CGRect(x = x + 20, 0, starSize, starSize);
            threeStar.Frame = new CGRect(x = x + 20, 0, starSize, starSize);
            fourStar.Frame = new CGRect(x = x + 20, 0, starSize, starSize);
            fiveStar.Frame = new CGRect(x = x + 20, 0, starSize, starSize);
        }

        private void SetRatingBarDefault()
        {
            //int x = (int)(App.ScreenSize.Width - ratingBarWidth) / 2;
            int x = 0;
            oneStar.Frame = new CGRect(x, 0, starSize, starSize);
            twoStar.Frame = new CGRect(x = x + 35, 0, starSize, starSize);
            threeStar.Frame = new CGRect(x = x + 35, 0, starSize, starSize);
            fourStar.Frame = new CGRect(x = x + 35, 0, starSize, starSize);
            fiveStar.Frame = new CGRect(x = x + 35, 0, starSize, starSize);
        }

        private void AddStarInView()
        {
            rateView.AddSubview(oneStar);
            rateView.AddSubview(twoStar);
            rateView.AddSubview(threeStar);
            rateView.AddSubview(fourStar);
            rateView.AddSubview(fiveStar);
        }

        private void SetBlankStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }


        private void SetOneStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarRed), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }

        private void SetTwoStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarRed), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarRed), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }

        private void SetThreeStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarOrange), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarOrange), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarOrange), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }

        private void SetFourStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarYellow), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarYellow), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarYellow), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarYellow), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarBlank), UIControlState.Normal);
        }

        private void SetFiveStarRating()
        {
            oneStar.SetImage(UIImage.FromFile(IconStarGreen), UIControlState.Normal);
            twoStar.SetImage(UIImage.FromFile(IconStarGreen), UIControlState.Normal);
            threeStar.SetImage(UIImage.FromFile(IconStarGreen), UIControlState.Normal);
            fourStar.SetImage(UIImage.FromFile(IconStarGreen), UIControlState.Normal);
            fiveStar.SetImage(UIImage.FromFile(IconStarGreen), UIControlState.Normal);
        }
    }
}

Step 6 - Now u can use the rating bar in XAML files as:
 <?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="RatingBarDemo.Views.RatingBarPage"
       xmlns:uc="clr-namespace:RatingBarDemo.Controls" >
       <uc:CustomRatingBar x:Name="customRatingBar" MaxStars="5"
RatingChanged="OnRatingChanged" IsReadonly="false" />
       </ContentPage>
 
Note- This custom Rating Bar having following features-

     1- This Control having property to set it size small or large.
            2- You can set a maximum number of stars.
            3- You can change rating bar colour.
            4- These all facilities in both Android and iOS also.
 
 
Find the Sample for the same here.