Search This Blog

Sunday 10 December 2017

Search View Example In xamarin android

MainActivity.cs:

using Android.App;
using Android.Widget;
using Android.OS;
using Android.Content;
using System.Linq;

namespace DemoG
{
    [Activity(Label = "DemoG", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        fragment_List newFragment;
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
             SetContentView (Resource.Layout.Main);

            SearchView _search = FindViewById<SearchView>(Resource.Id.sv_name);
            _search.QueryTextChange += _search_QueryTextChange;

            newFragment = new fragment_List();
            var ft = FragmentManager.BeginTransaction();
            ft.Add(Resource.Id.fragment_container, newFragment);
            ft.Commit();
        }

        private void _search_QueryTextChange(object sender, SearchView.QueryTextChangeEventArgs e)
        {
            var searchedNames = (from a in newFragment.objname where a.PersonName.ToString().Contains(e.NewText.ToString()) select a).ToList();
            newFragment.mAdapter.setLatestNames(searchedNames);
            newFragment.mAdapter.NotifyDataSetChanged();
        }
    }
}

========================================================================
fragment_List.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Android.Support.V7.Widget;

namespace DemoG
{
    public class fragment_List : Fragment
    {
        public RecyclerView mRecyclerView;
        public RecyclerView.LayoutManager mLayoutManager;
        public PhotoAlbumAdapter mAdapter;
        public List<PersonModel> objname;
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Create your fragment here
        }

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            View view = inflater.Inflate(Resource.Layout.recylerView_Fragment, container, false);
            mRecyclerView = view.FindViewById<RecyclerView>(Resource.Id.recyclerView);
            objname = new List<PersonModel>();
            objname.Add(new PersonModel { PersonName = "chiru", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "Arun", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "Venkatrao", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "subbu", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "ramesh", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "123", PersonPhoneNumber = "123456789" });
            objname.Add(new PersonModel { PersonName = "old", PersonPhoneNumber = "123456789" });

            mAdapter = new PhotoAlbumAdapter(objname);
            // Get our RecyclerView layout:
            mLayoutManager = new LinearLayoutManager(this.Activity);
            mRecyclerView.SetLayoutManager(mLayoutManager);
            // Plug the adapter into the RecyclerView:
            mRecyclerView.SetAdapter(mAdapter);

            return view;
        }

        internal static fragment_List NewInstance()
        {
            return new fragment_List();
        }
    }
    public class PhotoViewHolder : RecyclerView.ViewHolder
    {
        public TextView Caption { get; private set; }
        public TextView _PhoneNumber { get; private set; }
        public PhotoViewHolder(View itemView, Action<int> listener): base(itemView)
        {
            Caption = itemView.FindViewById<TextView>(Resource.Id.tv_personName);
            _PhoneNumber = itemView.FindViewById<TextView>(Resource.Id.tv_personPhoneNumber);
            itemView.Click += (sender, e) => listener(base.LayoutPosition);
        }
    }

    public class PhotoAlbumAdapter : RecyclerView.Adapter
    {
        public event EventHandler<int> ItemClick;
        public List<PersonModel> mPhotoAlbum;

        public PhotoAlbumAdapter(List<PersonModel> photoAlbum)
        {
            mPhotoAlbum = photoAlbum;
        }

        public void setLatestNames(List<PersonModel> photoAlbum)
        {
            this.mPhotoAlbum = photoAlbum;
        }

        // Create a new photo CardView (invoked by the layout manager):
        public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
        {
            View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.details, parent, false);

            PhotoViewHolder vh = new PhotoViewHolder(itemView, OnClick);
            return vh;
        }

        // Fill in the contents of the photo card (invoked by the layout manager):
        public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
        {
            PhotoViewHolder vh = holder as PhotoViewHolder;
            vh.Caption.Text = mPhotoAlbum[position].PersonName;
            vh._PhoneNumber.Text = mPhotoAlbum[position].PersonPhoneNumber;
        }

        // Return the number of photos available in the photo album:
        public override int ItemCount
        {
            get { return mPhotoAlbum.Count; }
        }

        // Raise an event when the item-click takes place:
        void OnClick(int position)
        {
            if (ItemClick != null)
                ItemClick(this, position);
        }
    }
}
=======================================================================
Main.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <SearchView
    android:id="@+id/sv_name"
    android:queryHint="Search"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"/>
  <FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  </FrameLayout>
</LinearLayout>
========================================================================
PersonModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace DemoG
{
   public class PersonModel
    {
        public string PersonName { set; get; }
        public string PersonPhoneNumber { set; get; }
    }
}
========================================================================
recylerView_Fragment.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:scrollbars="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>
========================================================================
details.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:text="name"
    android:layout_weight="1"
    android:id="@+id/tv_personName"
    android:textSize="18dp"/>
  <TextView
    android:layout_marginRight="10dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="name"
    android:id="@+id/tv_personPhoneNumber"
    android:textSize="18dp"/>
</LinearLayout>

Tuesday 7 February 2017

validation

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace App2
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            et.TextChanged += Et_TextChanged;
        }
        private void Et_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (e.NewTextValue != "")
            {
                if (e.NewTextValue.StartsWith("."))
                {
                    return;
                }
                long value = (long)Convert.ToDouble(e.NewTextValue);
                if (value <= 90.0)
                {
                    et.Text = e.NewTextValue;
                }
                else
                {
                    et.Text = et.Text.Remove(et.Text.Length - 1);
                }
                if (et.Text.Length >= 3)
                {
                    char[] chars = et.Text.ToCharArray();
                    if (chars[2] != '.')
                    {
                        et.Text = et.Text.Remove(et.Text.Length - 1);
                    }
                    else
                    {
                        if (chars[2] == '.')
                        {
                            if (et.Text.Length <= 5)
                            {
                                et.Text = e.NewTextValue;
                            }
                            else
                            {
                                et.Text = et.Text.Remove(et.Text.Length - 1);
                            }
                        }
                        else
                        {
                            et.Text = et.Text.Remove(et.Text.Length - 1);
                        }
                    }
                }
            }
        }
    }
}

Wednesday 1 February 2017

pdf Android dependecy

https://forums.xamarin.com/discussion/25746/how-to-open-pdf-in-xamarin-forms
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.IO;
using System.Threading.Tasks;
using App2.Droid;
using Java.IO;
using Xamarin.Forms;

[assembly: Xamarin.Forms.Dependency(typeof(SaveAndLoad_Android))]
namespace App2.Droid
{
    public class SaveAndLoad_Android : ISaveAndLoad
    {
        public async Task Save(string fileName, String contentType, MemoryStream stream)
        {
            string root = null;
            if (Android.OS.Environment.IsExternalStorageEmulated)
            {
                root = Android.OS.Environment.ExternalStorageDirectory.ToString();
            }
            else
                root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);

            Java.IO.File myDir = new Java.IO.File(root + "/Documents");
            myDir.Mkdir();

            Java.IO.File file = new Java.IO.File(myDir, fileName);

            if (file.Exists()) file.Delete();

            try
            {
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(stream.ToArray());

                outs.Flush();
                outs.Close();
            }
            catch (Exception e)
            {
            }
            if (file.Exists())
            {
                Android.Net.Uri path = Android.Net.Uri.FromFile(file);
                string extension = Android.Webkit.MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString());
                string mimeType = Android.Webkit.MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
                Intent intent = new Intent(Intent.ActionView);
                intent.SetDataAndType(path, mimeType);
                Forms.Context.StartActivity(Intent.CreateChooser(intent, "Choose App"));
            }
        }
    }
}

pdf PCL Interface

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace App2
{
   public interface ISaveAndLoad
    {
        Task Save(string filename, string contentType, MemoryStream stream);
    }
}

pdfButton Click

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xfinium.Pdf;
using Xfinium.Pdf.Graphics;

namespace App2
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            btnRecord.Clicked += BtnRecord_Clicked;
        }

        private void BtnRecord_Clicked(object sender, EventArgs e)
        {
            PdfFixedDocument document = new PdfFixedDocument();
            PdfPage page = document.Pages.Add();
            PdfStandardFont helvetica = new PdfStandardFont(PdfStandardFontFace.Helvetica, 24);
            PdfBrush brush = new PdfBrush();
            page.Graphics.DrawString("World is a common name for the whole of human civilization, specifically human experience, history, or the human condition in general, worldwide, i.e. anywhere on Earth or pertaining to anywhere on earth.", helvetica, brush, 100, 100);
            MemoryStream stream = new MemoryStream();
            document.Save(stream);

            DependencyService.Get<ISaveAndLoad>().Save("HelloWorld.pdf", "application/pdf", stream);
        }
    }
}

Sunday 15 January 2017

DatePicker

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace App2
{
    public partial class MainPage : ContentPage
    {
        bool dateselected = false;
        public MainPage()
        {
            InitializeComponent();
            dPicker.DateSelected += DPicker_DateSelected;
            dPicker.MaximumDate = DateTime.Now;
            dPicker.Unfocused += DPicker_Unfocused;

            btnclear.Clicked += Btnclear_Clicked;
        }
        private void DPicker_DateSelected(object sender, DateChangedEventArgs e)
        {
            dateselected = true;
            lblDate.Text = dPicker.Date.ToString();
        }
        private void DPicker_Unfocused(object sender, FocusEventArgs e)
        {
            if (dateselected == true)
            {
                lblDate.Text = dPicker.Date.ToString();
            }
            else
            {
                lblDate.Text = DateTime.Now.ToString();
            }
        }
        private void Btnclear_Clicked(object sender, EventArgs e)
        {
            lblDate.Text = "- - - - -";
        }
    }
}


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App2"
             x:Class="App2.MainPage">
  <ContentPage.Content>
    <StackLayout>
      <DatePicker x:Name="dPicker" VerticalOptions="Center" HorizontalOptions="Center"/>
      <Label x:Name="lblDate"  FontSize="20" Text="- - - - -" VerticalOptions="Center" HorizontalOptions="Center"/>
        <Button x:Name="btnclear" Text="Clear" VerticalOptions="Center" HorizontalOptions="Center"/>
    </StackLayout>
  </ContentPage.Content>
 
</ContentPage>

Thursday 5 January 2017

Range slider Ui Class

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:CustomSlider"
             x:Class="CustomSlider.MainPage">
   <ContentPage.Content>
<StackLayout>
<StackLayout Orientation="Vertical" >
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<Label x:Name="SeverityleftValue" VerticalOptions="Start" Text="0" FontSize="20" />
<Label Text="-" VerticalOptions="Center" FontSize="20" />
<Label x:Name="SeverityrightValue" VerticalOptions="End" Text="10" FontSize="20" />
</StackLayout>
<local:RangeSlider x:Name="rsldrSeverity" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"  ActiveColorValue="#00A0D4" NormalColorValue="#95D0E7"/>
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<Label x:Name="DurationleftValue" VerticalOptions="Start" Text="0" FontSize="20" />
<Label Text="-" VerticalOptions="Center" FontSize="20" />
<Label x:Name="DurationrightValue" VerticalOptions="End" Text="25" FontSize="20" />
</StackLayout>
<local:RangeSlider x:Name="rsldrDuraton" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"  ActiveColorValue="#06A330" NormalColorValue="#CEEBD1"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace CustomSlider
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();

rsldrSeverity.LowerValue = 0;
rsldrSeverity.UpperValue = 10;
rsldrSeverity.MinimumValue = 0;
rsldrSeverity.MaximumValue = 10;
rsldrSeverity.TextSize = 20;
rsldrSeverity.BarHeight = 10;

rsldrDuraton.BarHeight = 10;

rsldrDuraton.SliderNameValue = "IsDuration";
rsldrSeverity.SliderNameValue = "IsSeverity";

//rsldrDuraton.ActiveColorValue = Color.FromRgb(6, 163, 48);
//rsldrDuraton.NormalColorValue = Color.FromRgb(206,235,209);

rsldrDuraton.LowerValue = 0;
rsldrDuraton.UpperValue = 25;
rsldrDuraton.MinimumValue = 0;
rsldrDuraton.MaximumValue = 25;
rsldrDuraton.TextSize = 20;


//rangeSlider.ShowTextAboveThumbs = true;

rsldrSeverity.LowerValueChanged += RangeSlider_LowerValueChanged;
rsldrSeverity.UpperValueChanged += RangeSlider_UpperValueChanged;




rsldrDuraton.LowerValueChanged += RsldrDuraton_LowerValueChanged;
rsldrDuraton.UpperValueChanged += RsldrDuraton_UpperValueChanged;

}
private void RangeSlider_UpperValueChanged(object sender, EventArgs e)
{
int myIntRight = (int)Math.Ceiling(((sender as RangeSlider).UpperValue));
SeverityrightValue.Text = myIntRight.ToString();
}

private void RangeSlider_LowerValueChanged(object sender, EventArgs e)
{
int myIntLeft = (int)Math.Ceiling(((sender as RangeSlider).LowerValue));
SeverityleftValue.Text = myIntLeft.ToString();
}

void RsldrDuraton_UpperValueChanged(object sender, EventArgs e)
{
int myIntRightDuration = (int)Math.Ceiling(((sender as RangeSlider).UpperValue));
DurationrightValue.Text = myIntRightDuration > 24 ? "24+" : myIntRightDuration.ToString();
}

void RsldrDuraton_LowerValueChanged(object sender, EventArgs e)
{
int myIntLeftDuration = (int)Math.Ceiling(((sender as RangeSlider).LowerValue));
DurationleftValue.Text = myIntLeftDuration.ToString();
}
}
}

Range Slider Interface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace CustomSlider
{
   public class RangeSlider : View
    {
        public const string LowerValuePropertyName = "LowerValue";
        public const string MaximumValuePropertyName = "MaximumValue";
        public const string MinimumValuePropertyName = "MinimumValue";
        public const string UpperValuePropertyName = "UpperValue";
        public const string MinThumbHiddenPropertyName = "MinThumbHidden";
        public const string MaxThumbHiddenPropertyName = "MaxThumbHidden";
        public const string StepValuePropertyName = "StepValue";
        public const string StepValueContinuouslyPropertyName = "StepValueContinuously";
        public const string BarHeightPropertyName = "BarHeight";
        public const string ShowTextAboveThumbsPropertyName = "ShowTextAboveThumbs";
        public const string TextSizePropertyName = "TextSize";
        public const string TextFormatPropertyName = "TextFormat";
        public const string FormatLabelPropertyName = "FormatLabel";
        public const string ActiveColorPropertyName = "ActiveColor";
        public const string NormalColorPropertyName = "NormalColor";
        public const string SliderPropertyName = "SliderName";
        //public const string LowerThumbImagePropertyName = "LowerThumbImage";
        //public const string UpperThumbImagePropertyName = "UpperThumbImage";


        //public static BindableProperty LowerThumbImageProperty =
        // BindableProperty.Create(LowerThumbImagePropertyName, typeof(string), typeof(RangeSlider), null);

        //public static BindableProperty UpperThumbImageProperty =
        // BindableProperty.Create(UpperThumbImagePropertyName, typeof(string), typeof(RangeSlider), null);

        public static BindableProperty ActiveColorProperty =
            BindableProperty.Create(ActiveColorPropertyName, typeof(Color), typeof(RangeSlider), Color.FromRgb(0, 160, 212));

        public static BindableProperty NormalColorProperty =
            BindableProperty.Create(NormalColorPropertyName, typeof(Color), typeof(RangeSlider), Color.FromRgb(149, 208, 231));

        public static readonly BindableProperty LowerValueProperty =
            BindableProperty.Create(LowerValuePropertyName, typeof(float), typeof(RangeSlider), 0f);

        public static readonly BindableProperty MaximumValueProperty =
            BindableProperty.Create(MaximumValuePropertyName, typeof(float), typeof(RangeSlider), 0f);

        public static readonly BindableProperty MaxThumbHiddenProperty =
            BindableProperty.Create(MaxThumbHiddenPropertyName, typeof(bool), typeof(RangeSlider), false);

        public static readonly BindableProperty MinimumValueProperty =
            BindableProperty.Create(MinimumValuePropertyName, typeof(float), typeof(RangeSlider), 0f);

        public static readonly BindableProperty MinThumbHiddenProperty =
            BindableProperty.Create(MinThumbHiddenPropertyName, typeof(bool), typeof(RangeSlider), false);

        public static readonly BindableProperty StepValueContinuouslyProperty =
            BindableProperty.Create(StepValueContinuouslyPropertyName, typeof(bool), typeof(RangeSlider), false);

        public static readonly BindableProperty StepValueProperty =
            BindableProperty.Create(StepValuePropertyName, typeof(float), typeof(RangeSlider), 0f);

        public static readonly BindableProperty UpperValueProperty =
            BindableProperty.Create(UpperValuePropertyName, typeof(float), typeof(RangeSlider), 0f);

        public static readonly BindableProperty BarHeightProperty =
            BindableProperty.Create(BarHeightPropertyName, typeof(int), typeof(RangeSlider), 0);

        public static readonly BindableProperty ShowTextAboveThumbsProperty =
            BindableProperty.Create(ShowTextAboveThumbsPropertyName, typeof(bool), typeof(RangeSlider), false);

        public static readonly BindableProperty TextSizeProperty =
            BindableProperty.Create(TextSizePropertyName, typeof(double), typeof(RangeSlider), 10D);

        public static readonly BindableProperty TextFormatProperty =
            BindableProperty.Create(TextFormatPropertyName, typeof(string), typeof(RangeSlider), "F0");

        public static readonly BindableProperty FormatLabelProperty =
            BindableProperty.Create(FormatLabelPropertyName, typeof(Func<Thumb, float, string>), typeof(RangeSlider), null);


        public static readonly BindableProperty sliderNamePro =
          BindableProperty.Create(SliderPropertyName, typeof(string), typeof(RangeSlider), "F0");


        //public string LowerThumbImageValue
        //{
        // get { return (string)GetValue(LowerThumbImageProperty); }
        // set { SetValue(LowerThumbImageProperty, value); }
        //}

        //public string UpperThumbImageValue
        //{
        // get { return (string)GetValue(UpperThumbImageProperty); }
        // set { SetValue(UpperThumbImageProperty, value); }
        //}

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

        public string SliderNameValue
        {
            get { return (string)GetValue(sliderNamePro); }
            set { SetValue(sliderNamePro, value); }
        }
             

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

        public float MinimumValue
        {
            get { return (float)GetValue(MinimumValueProperty); }
            set { SetValue(MinimumValueProperty, value); }
        }

        public float MaximumValue
        {
            get { return (float)GetValue(MaximumValueProperty); }
            set { SetValue(MaximumValueProperty, value); }
        }

        public float LowerValue
        {
            get { return (float)GetValue(LowerValueProperty); }
            set { SetValue(LowerValueProperty, value); }
        }

        public float UpperValue
        {
            get { return (float)GetValue(UpperValueProperty); }
            set { SetValue(UpperValueProperty, value); }
        }

        public bool MinThumbHidden
        {
            get { return (bool)GetValue(MinThumbHiddenProperty); }
            set { SetValue(MinThumbHiddenProperty, value); }
        }

        public bool MaxThumbHidden
        {
            get { return (bool)GetValue(MaxThumbHiddenProperty); }
            set { SetValue(MaxThumbHiddenProperty, value); }
        }

        public float StepValue
        {
            get { return (float)GetValue(StepValueProperty); }
            set { SetValue(StepValueProperty, value); }
        }

        public bool StepValueContinuously
        {
            get { return (bool)GetValue(StepValueContinuouslyProperty); }
            set { SetValue(StepValueContinuouslyProperty, value); }
        }

        public int? BarHeight
        {
            get { return (int?)GetValue(BarHeightProperty); }
            set { SetValue(BarHeightProperty, value); }
        }

        public bool ShowTextAboveThumbs
        {
            get { return (bool)GetValue(ShowTextAboveThumbsProperty); }
            set { SetValue(ShowTextAboveThumbsProperty, value); }
        }

        [TypeConverter(typeof(FontSizeConverter))]
        public double TextSize
        {
            get { return (double)GetValue(TextSizeProperty); }
            set { SetValue(TextSizeProperty, value); }
        }

        public string TextFormat
        {
            get { return (string)GetValue(TextFormatProperty); }
            set { SetValue(TextFormatProperty, value); }
        }

        public Func<Thumb, float, string> FormatLabel
        {
            get { return (Func<Thumb, float, string>)GetValue(FormatLabelProperty); }
            set { SetValue(FormatLabelProperty, value); }
        }

        public event EventHandler LowerValueChanged;
        public event EventHandler UpperValueChanged;
        public event EventHandler DragStarted;
        public event EventHandler DragCompleted;

        public void OnLowerValueChanged(float value)
        {
            LowerValue = value;
            LowerValueChanged?.Invoke(this, EventArgs.Empty);
        }

        public void OnUpperValueChanged(float value)
        {
            UpperValue = value;
            UpperValueChanged?.Invoke(this, EventArgs.Empty);
        }

        public void OnDragStarted()
        {
            DragStarted?.Invoke(this, EventArgs.Empty);
        }

        public void OnDragCompleted()
        {
            DragCompleted?.Invoke(this, EventArgs.Empty);
        }
    }
}

Range slider Render Android

using System;
using Android.Runtime;
using CustomSlider;
using CustomSlider.Droid;
using Xamarin.Forms.Platform.Android;
using System.ComponentModel;
using Xamarin.Forms;

[assembly: ExportRenderer(typeof(RangeSlider), typeof(RangeSliderRenderer))]
namespace CustomSlider.Droid
{
    [Preserve(AllMembers = true)]
    public class RangeSliderRenderer : ViewRenderer<RangeSlider, RangeSliderControl>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<RangeSlider> e)
        {
            base.OnElementChanged(e);
            if (Element == null)
                return;
            if (Control == null)
            {
                var rangeSeekBar = new RangeSliderControl(Context, Element.ActiveColorValue.ToAndroid(), Element.NormalColorValue.ToAndroid(),Element.SliderNameValue)
                {
                    NotifyWhileDragging = true,
                    TextAboveThumbsColor = Android.Graphics.Color.Black
                };
                rangeSeekBar.LowerValueChanged += RangeSeekBarLowerValueChanged;
                rangeSeekBar.UpperValueChanged += RangeSeekBarUpperValueChanged;
                rangeSeekBar.DragStarted += RangeSeekBarDragStarted;
                rangeSeekBar.DragCompleted += RangeSeekBarDragCompleted;
                SetNativeControl(rangeSeekBar);
            }
            UpdateControl(Control, Element);
        }

        private void RangeSeekBarDragCompleted(object sender, EventArgs e)
        {
            Element.OnDragCompleted();
        }

        private void RangeSeekBarDragStarted(object sender, EventArgs e)
        {
            Element.OnDragStarted();
        }

        private void UpdateControl(RangeSliderControl control, RangeSlider element)
        {
            control.SetSelectedMinValue(element.LowerValue);
            control.SetSelectedMaxValue(element.UpperValue);
            control.SetRangeValues(element.MinimumValue, element.MaximumValue);
            control.MinThumbHidden = element.MinThumbHidden;
            control.MaxThumbHidden = element.MaxThumbHidden;
            control.StepValue = element.StepValue;
            control.StepValueContinuously = element.StepValueContinuously;
            if (element.BarHeight.HasValue)
                control.SetBarHeight(element.BarHeight.Value);
            control.ShowTextAboveThumbs = element.ShowTextAboveThumbs;
            control.TextSizeInSp = (int)Font.SystemFontOfSize(element.TextSize).ToScaledPixel();
            control.TextFormat = element.TextFormat;
            control.FormatLabel = element.FormatLabel;
            control.ActivateOnDefaultValues = true;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            switch (e.PropertyName)
            {
                case RangeSlider.LowerValuePropertyName:
                    Control.SetSelectedMinValue(Element.LowerValue);
                    break;
                case RangeSlider.UpperValuePropertyName:
                    Control.SetSelectedMaxValue(Element.UpperValue);
                    break;
                case RangeSlider.MinimumValuePropertyName:
                case RangeSlider.MaximumValuePropertyName:
                    Control.SetRangeValues(Element.MinimumValue, Element.MaximumValue);
                    break;
                case RangeSlider.MaxThumbHiddenPropertyName:
                    Control.MaxThumbHidden = Element.MaxThumbHidden;
                    break;
                case RangeSlider.MinThumbHiddenPropertyName:
                    Control.MinThumbHidden = Element.MinThumbHidden;
                    break;
                case RangeSlider.StepValuePropertyName:
                    Control.StepValue = Element.StepValue;
                    break;
                case RangeSlider.StepValueContinuouslyPropertyName:
                    Control.StepValueContinuously = Element.StepValueContinuously;
                    break;
                case RangeSlider.BarHeightPropertyName:
                    if (Element.BarHeight.HasValue)
                        Control.SetBarHeight(Element.BarHeight.Value);
                    break;
                case RangeSlider.ShowTextAboveThumbsPropertyName:
                    Control.ShowTextAboveThumbs = Element.ShowTextAboveThumbs;
                    ForceFormsLayout();
                    break;
                case RangeSlider.TextSizePropertyName:
                    Control.TextSizeInSp = (int)Font.SystemFontOfSize(Element.TextSize).ToScaledPixel();
                    ForceFormsLayout();
                    break;
                case RangeSlider.TextFormatPropertyName:
                    Control.TextFormat = Element.TextFormat;
                    break;
                case RangeSlider.FormatLabelPropertyName:
                    Control.FormatLabel = Element.FormatLabel;
                    break;
            }
        }

        private void ForceFormsLayout()
        {
            //HACK to force Xamarin.Forms layout engine to update control size
            if (!Element.IsVisible) return;
            Element.IsVisible = false;
            Element.IsVisible = true;
        }

        private void RangeSeekBarUpperValueChanged(object sender, EventArgs e)
        {
            Element.OnUpperValueChanged(Control.GetSelectedMaxValue());
        }

        private void RangeSeekBarLowerValueChanged(object sender, EventArgs e)
        {
            Element.OnLowerValueChanged(Control.GetSelectedMinValue());
        }
    }
}

Range SliderControl

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Util;
using Android.Content.Res;
using System.Globalization;

namespace CustomSlider.Droid
{
    public class RangeSliderControl : ImageView
    {
        public Color DefaultActiveColor;// = Color.Rgb(0, 160, 212);
        public Color DefaultNormalColor;// = Color.Rgb(149, 208, 231);

        public string thumbNormal;
        public string thumbPressed;
     

        public string sliderName;


        /// <summary>
        ///     An invalid pointer id.C:\Users\VenkatRao\Downloads\CustomSlider\CustomSlider\CustomSlider.iOS\RangeSliderControl.cs
        /// </summary>
        public const int InvalidPointerId = 255;

        // Localized constants from MotionEvent for compatibility
        // with API < 8 "Froyo".
        public const int ActionPointerIndexMask = 0x0000ff00, ActionPointerIndexShift = 8;

        public const int DefaultMinimum = 0;
        public const int DefaultMaximum = 100;
        public const int HeightInDp = 16;
        public const int TextLateralPaddingInDp = 3;

        private const int InitialPaddingInDp = 8;
        private const int DefaultTextSizeInSp = 15;
        private const int DefaultTextDistanceToButtonInDp = 8;
        private const int DefaultTextDistanceToTopInDp = 8;

        private const int DefaultStepValue = 0;

        private const int LineHeightInDp = 1;
        private readonly Paint _paint = new Paint(PaintFlags.AntiAlias);
        private readonly Paint _shadowPaint = new Paint();
        private readonly Matrix _thumbShadowMatrix = new Matrix();
        private readonly Path _translatedThumbShadowPath = new Path();

        private int _activePointerId = InvalidPointerId;
        private int _distanceToTop;

        private float _downMotionX;
        private float _internalPad;

        private bool _isDragging;

        private float _padding;
        private Thumb? _pressedThumb;
        private RectF _rect;

        private int _scaledTouchSlop;

        private int _textOffset;
        private int _textSize;
        private float _thumbHalfHeight;

        private float _thumbHalfWidth;
        private int _thumbShadowBlur;
        private Path _thumbShadowPath;
        protected float AbsoluteMinValue, AbsoluteMaxValue;
        protected float MinDeltaForDefault = 0;
        protected float NormalizedMaxValue = 1f;
        protected float NormalizedMinValue;
        private Color _activeColor;
        private Color _normalColor;
        private bool _minThumbHidden;
        private bool _maxThumbHidden;
        private bool _showTextAboveThumbs;
        private float _barHeight;
        private string _textFormat = "F0";

        private float MinToMaxRange
        {
            get
            {
                return AbsoluteMaxValue - AbsoluteMinValue;
            }
        }

        protected RangeSliderControl(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        public RangeSliderControl(Context context) : base(context)
        {
            Init(context, null);
        }

        public RangeSliderControl(Context context, Color active, Color normal, string name) : base(context)
        {
            DefaultActiveColor = active;

            DefaultNormalColor = normal;
            sliderName = name;

            //thumbNormal = NormalImage;

            //thumbPressed = ActiveImage;

            //, string NormalImage, string ActiveImage

            Init(context, null);
        }

        public RangeSliderControl(Context context, IAttributeSet attrs) : base(context, attrs)
        {
            Init(context, attrs);
        }

        public RangeSliderControl(Context context, IAttributeSet attrs, int defStyleAttr)
                : base(context, attrs, defStyleAttr)
        {
            Init(context, attrs);
        }

        public RangeSliderControl(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes)
                : base(context, attrs, defStyleAttr, defStyleRes)
        {
            Init(context, attrs);
        }

        public bool ActivateOnDefaultValues { get; set; }

        public Color ActiveColor
        {
            get { return _activeColor; }
            set
            {
                _activeColor = value;
                Invalidate();
            }
        }

        public Color NormalColor
        {
            get { return _normalColor; }
            set
            {
                _normalColor = value;
                Invalidate();
            }
        }


        public Func<Thumb, float, string> FormatLabel { get; set; }

        public override bool Enabled
        {
            get
            {
                return base.Enabled;
            }
            set
            {
                base.Enabled = value;
                Invalidate();
            }
        }

        public bool AlwaysActive { get; set; }
        public Color DefaultColor { get; set; }
        public bool ShowLabels { get; set; }

        public int TextSizeInSp
        {
            get { return PixelUtil.PxToSp(Context, _textSize); }
            set
            {
                _textSize = PixelUtil.SpToPx(Context, value);
                UpdateTextOffset();
                SetBarHeight(_barHeight);
                RequestLayout();
                Invalidate();
            }
        }

        public bool ShowTextAboveThumbs
        {
            get { return _showTextAboveThumbs; }
            set
            {
                _showTextAboveThumbs = value;
                UpdateTextOffset();
                SetBarHeight(_barHeight);
                RequestLayout();
                Invalidate();
            }
        }

        public string TextFormat
        {
            get { return _textFormat; }
            set
            {
                _textFormat = string.IsNullOrWhiteSpace(value) ? "F0" : value;
                Invalidate();
            }
        }

        public bool MinThumbHidden
        {
            get { return _minThumbHidden; }
            set
            {
                _minThumbHidden = value;
                Invalidate();
            }
        }

        public bool MaxThumbHidden
        {
            get { return _maxThumbHidden; }
            set
            {
                _maxThumbHidden = value;
                Invalidate();
            }
        }

        public Color TextAboveThumbsColor { get; set; }
        public Bitmap ThumbDisabledImage { get; set; }

        public Bitmap ThumbImage { get; set; }
        public Bitmap ThumbPressedImage { get; set; }

        public bool ThumbShadow { get; set; }
        public int ThumbShadowXOffset { get; set; }
        public int ThumbShadowYOffset { get; set; }

        /// <summary>
        ///     Should the widget notify the listener callback while the user is still dragging a thumb? Default is false.
        /// </summary>
        public bool NotifyWhileDragging { get; set; }

        /// <summary>
        ///     default 0.0 (disabled)
        /// </summary>
        public float StepValue { get; set; }

        /// <summary>
        /// If false the slider will move freely with the tounch. When the touch ends, the value will snap to the nearest step value
        /// If true the slider will stay in its current position until it reaches a new step value.
        /// default false
        /// </summary>
        public bool StepValueContinuously { get; set; }

        private float ExtractNumericValueFromAttributes(TypedArray a, int attribute, int defaultValue)
        {
            var tv = a.PeekValue(attribute);
            return tv == null ? defaultValue : a.GetFloat(attribute, defaultValue);
        }

        private void Init(Context context, IAttributeSet attrs)
        {
            int thumbNormal = Resource.Drawable.seek_thumb_normal_blue;
            int  thumbPressed= Resource.Drawable.seek_thumb_Select_blue;
            int  thumbDisabled= Resource.Drawable.seek_thumb_disabled;
            if (sliderName == "IsSeverity")
            {
                thumbNormal = Resource.Drawable.seek_thumb_normal_blue;
                thumbPressed = Resource.Drawable.seek_thumb_Select_blue;
                thumbDisabled = Resource.Drawable.seek_thumb_disabled;
            }
            else if (sliderName == "IsDuration")
            {

                thumbNormal = Resource.Drawable.seek_thumb_normal_Green;
                thumbPressed = Resource.Drawable.seek_thumb_Select_Green;
                thumbDisabled = Resource.Drawable.seek_thumb_disabled;
            }


            Color thumbShadowColor;
            var defaultShadowColor = Color.Argb(75, 0, 0, 0);
            var defaultShadowYOffset = PixelUtil.DpToPx(context, 2);
            var defaultShadowXOffset = PixelUtil.DpToPx(context, 0);
            var defaultShadowBlur = PixelUtil.DpToPx(context, 2);

            _distanceToTop = PixelUtil.DpToPx(context, DefaultTextDistanceToTopInDp);

            if (attrs == null)
            {
                SetRangeToDefaultValues();
                _internalPad = PixelUtil.DpToPx(context, InitialPaddingInDp);
                _barHeight = PixelUtil.DpToPx(context, LineHeightInDp);
                ActiveColor = DefaultActiveColor;
                DefaultColor = DefaultNormalColor;//Color.Gray;
                AlwaysActive = false;
                ShowTextAboveThumbs = true;
                TextAboveThumbsColor = Color.White;
                thumbShadowColor = defaultShadowColor;
                ThumbShadowXOffset = defaultShadowXOffset;
                ThumbShadowYOffset = defaultShadowYOffset;
                _thumbShadowBlur = defaultShadowBlur;
                ActivateOnDefaultValues = false;
                TextSizeInSp = DefaultTextSizeInSp;
            }
            else
            {
                var a = Context.ObtainStyledAttributes(attrs, Resource.Styleable.RangeSliderControl, 0, 0);
                try
                {
                    SetRangeValues(ExtractNumericValueFromAttributes(a, Resource.Styleable.RangeSliderControl_absoluteMinValue, DefaultMinimum),
                    ExtractNumericValueFromAttributes(a, Resource.Styleable.RangeSliderControl_absoluteMaxValue, DefaultMaximum));
                    ShowTextAboveThumbs = a.GetBoolean(Resource.Styleable.RangeSliderControl_valuesAboveThumbs, true);
                    TextAboveThumbsColor = a.GetColor(Resource.Styleable.RangeSliderControl_textAboveThumbsColor, Color.White);
                    MinThumbHidden = a.GetBoolean(Resource.Styleable.RangeSliderControl_minThumbHidden, false);
                    MaxThumbHidden = a.GetBoolean(Resource.Styleable.RangeSliderControl_maxThumbHidden, false);
                    ShowLabels = a.GetBoolean(Resource.Styleable.RangeSliderControl_showLabels, true);
                    _internalPad = a.GetDimensionPixelSize(Resource.Styleable.RangeSliderControl_internalPadding, InitialPaddingInDp);
                    _barHeight = a.GetDimensionPixelSize(Resource.Styleable.RangeSliderControl_barHeight, LineHeightInDp);
                    ActiveColor = a.GetColor(Resource.Styleable.RangeSliderControl_activeColor, DefaultActiveColor);
                    //  NormalColor = a.GetColor(Resource.Styleable.RangeSliderControl_normalColor, DefaultNormalColor);
                    DefaultColor = a.GetColor(Resource.Styleable.RangeSliderControl_defaultColor, Color.Gray);
                    AlwaysActive = a.GetBoolean(Resource.Styleable.RangeSliderControl_alwaysActive, false);
                    StepValue = ExtractNumericValueFromAttributes(a,
                        Resource.Styleable.RangeSliderControl_stepValue, DefaultStepValue);
                    StepValueContinuously = a.GetBoolean(Resource.Styleable.RangeSliderControl_stepValueContinuously,
                        false);

                    var normalDrawable = a.GetDrawable(Resource.Styleable.RangeSliderControl_thumbNormal);
                    if (normalDrawable != null)
                    {
                        ThumbImage = BitmapUtil.DrawableToBitmap(normalDrawable);
                    }
                    var disabledDrawable = a.GetDrawable(Resource.Styleable.RangeSliderControl_thumbDisabled);
                    if (disabledDrawable != null)
                    {
                        ThumbDisabledImage = BitmapUtil.DrawableToBitmap(disabledDrawable);
                    }
                    var pressedDrawable = a.GetDrawable(Resource.Styleable.RangeSliderControl_thumbPressed);
                    if (pressedDrawable != null)
                    {
                        ThumbPressedImage = BitmapUtil.DrawableToBitmap(pressedDrawable);
                    }
                    ThumbShadow = a.GetBoolean(Resource.Styleable.RangeSliderControl_thumbShadow, false);
                    thumbShadowColor = a.GetColor(Resource.Styleable.RangeSliderControl_thumbShadowColor, defaultShadowColor);
                    ThumbShadowXOffset = a.GetDimensionPixelSize(Resource.Styleable.RangeSliderControl_thumbShadowXOffset, defaultShadowXOffset);
                    ThumbShadowYOffset = a.GetDimensionPixelSize(Resource.Styleable.RangeSliderControl_thumbShadowYOffset, defaultShadowYOffset);
                    _thumbShadowBlur = a.GetDimensionPixelSize(Resource.Styleable.RangeSliderControl_thumbShadowBlur, defaultShadowBlur);

                    ActivateOnDefaultValues = a.GetBoolean(Resource.Styleable.RangeSliderControl_activateOnDefaultValues, false);
                    TextSizeInSp = a.GetInt(Resource.Styleable.RangeSliderControl_textSize, DefaultTextSizeInSp);
                }
                finally
                {
                    a.Recycle();
                }
            }

            if (ThumbImage == null)
            {
                ThumbImage = BitmapFactory.DecodeResource(Resources, thumbNormal);
            }
            if (ThumbPressedImage == null)
            {
                ThumbPressedImage = BitmapFactory.DecodeResource(Resources, thumbPressed);
            }
            if (ThumbDisabledImage == null)
            {
                ThumbDisabledImage = BitmapFactory.DecodeResource(Resources, thumbDisabled);
            }

            _thumbHalfWidth = 0.5f * ThumbImage.Width;
            _thumbHalfHeight = 0.5f * ThumbImage.Height;

            SetBarHeight(_barHeight);

            // make RangeSliderControl focusable. This solves focus handling issues in case EditText widgets are being used along with the RangeSliderControl within ScrollViews.
            Focusable = true;
            FocusableInTouchMode = true;
            _scaledTouchSlop = ViewConfiguration.Get(Context).ScaledTouchSlop;

            if (ThumbShadow)
            {
                // We need to remove hardware acceleration in order to blur the shadow
                SetLayerType(LayerType.Software, null);
                _shadowPaint.Color = thumbShadowColor;
                _shadowPaint.SetMaskFilter(new BlurMaskFilter(_thumbShadowBlur, BlurMaskFilter.Blur.Normal));
                _thumbShadowPath = new Path();
                _thumbShadowPath.AddCircle(0, 0, _thumbHalfHeight, Path.Direction.Cw);
            }
        }

        public void SetBarHeight(float barHeight)
        {
            _barHeight = barHeight;
            if (_rect == null)
                _rect = new RectF(_padding,
                    _textOffset + _thumbHalfHeight - barHeight / 2,
                    Width - _padding,
                    _textOffset + _thumbHalfHeight + barHeight / 2);
            else
                _rect = new RectF(_rect.Left,
                    _textOffset + _thumbHalfHeight - barHeight / 2,
                    _rect.Right,
                    _textOffset + _thumbHalfHeight + barHeight / 2);
            Invalidate();
        }

        public void SetRangeValues(float minValue, float maxValue)
        {
            var oldMinValue = NormalizedToValue(NormalizedMinValue);
            var oldMaxValue = NormalizedToValue(NormalizedMaxValue);
            AbsoluteMinValue = minValue;
            AbsoluteMaxValue = maxValue;
            if (Math.Abs(MinToMaxRange) < float.Epsilon)
            {
                SetNormalizedMinValue(0f, true, true);
                SetNormalizedMaxValue(0f, true, true);
            }
            else
            {
                SetNormalizedMinValue(ValueToNormalized(oldMinValue), true, true);
                SetNormalizedMaxValue(ValueToNormalized(oldMaxValue), true, true);
            }
            Invalidate();
        }

        public void SetTextAboveThumbsColor(Color textAboveThumbsColor)
        {
            TextAboveThumbsColor = textAboveThumbsColor;
            Invalidate();
        }

        public void SetTextAboveThumbsColorResource(int resId)
        {
            SetTextAboveThumbsColor(Resources.GetColor(resId, Context.Theme));
        }


        /// <summary>
        /// only used to set default values when initialised from XML without any values specified
        /// </summary>
        private void SetRangeToDefaultValues()
        {
            AbsoluteMinValue = DefaultMinimum;
            AbsoluteMaxValue = DefaultMaximum;
        }

        public void ResetSelectedValues()
        {
            SetSelectedMinValue(AbsoluteMinValue);
            SetSelectedMaxValue(AbsoluteMaxValue);
        }

        /// <summary>
        /// Returns the absolute minimum value of the range that has been set at construction time.
        /// </summary>
        /// <returns>The absolute minimum value of the range.</returns>
        public float GetAbsoluteMinValue()
        {
            return AbsoluteMinValue;
        }

        /// <summary>
        /// Returns the absolute maximum value of the range that has been set at construction time.
        /// </summary>
        /// <returns>The absolute maximum value of the range.</returns>
        public float GetAbsoluteMaxValue()
        {
            return AbsoluteMaxValue;
        }

        /// <summary>
        /// Returns the currently selected min value.
        /// </summary>
        /// <returns>The currently selected min value.</returns>
        public float GetSelectedMinValue()
        {
            return NormalizedToValue(NormalizedMinValue);
        }

        /// <summary>
        /// Sets the currently selected minimum value. The widget will be Invalidated and redrawn.
        /// </summary>
        /// <param name="value">The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range.</param>
        public void SetSelectedMinValue(float value)
        {
            if (_pressedThumb == Thumb.Lower)
                return;
            // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing.
            SetNormalizedMinValue(Math.Abs(MinToMaxRange) < float.Epsilon
                ? 0f
                : ValueToNormalized(value), true, false);
        }

        /// <summary>
        /// Returns the currently selected max value.
        /// </summary>
        /// <returns>The currently selected max value.</returns>
        public float GetSelectedMaxValue()
        {
            return NormalizedToValue(NormalizedMaxValue);
        }

        /// <summary>
        /// Sets the currently selected maximum value. The widget will be Invalidated and redrawn.
        /// </summary>
        /// <param name="value">The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range.</param>
        public void SetSelectedMaxValue(float value)
        {
            if (_pressedThumb == Thumb.Upper)
                return;
            // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing.
            SetNormalizedMaxValue(Math.Abs(MinToMaxRange) < float.Epsilon
                ? 1f
                : ValueToNormalized(value), true, false);
        }

        /// <summary>
        /// Set the path that defines the shadow of the thumb. This path should be defined assuming
        /// that the center of the shadow is at the top left corner(0,0) of the canvas.The
        /// <see cref="DrawThumbShadow"/> method will place the shadow appropriately.
        /// </summary>
        /// <param name="thumbShadowPath">The path defining the thumb shadow</param>
        public void SetThumbShadowPath(Path thumbShadowPath)
        {
            _thumbShadowPath = thumbShadowPath;
        }

        /// <summary>
        ///     Handles thumb selection and movement. Notifies listener callback on certain evs.
        /// </summary>
        public override bool OnTouchEvent(MotionEvent ev)
        {
            if (!Enabled)
            {
                return false;
            }

            int pointerIndex;

            var action = ev.Action;
            switch (action & MotionEventActions.Mask)
            {
                case MotionEventActions.Down:
                    // Remember where the motion ev started
                    _activePointerId = ev.GetPointerId(ev.PointerCount - 1);
                    pointerIndex = ev.FindPointerIndex(_activePointerId);
                    _downMotionX = ev.GetX(pointerIndex);

                    _pressedThumb = EvalPressedThumb(_downMotionX);

                    // Only handle thumb presses.
                    if (_pressedThumb == null)
                    {
                        return base.OnTouchEvent(ev);
                    }

                    Pressed = true;
                    Invalidate();
                    OnStartTrackingTouch();
                    TrackTouchEvent(ev, StepValueContinuously);
                    AttemptClaimDrag();

                    break;
                case MotionEventActions.Move:
                    if (_pressedThumb != null)
                    {
                        if (_isDragging)
                        {
                            TrackTouchEvent(ev, StepValueContinuously);
                        }
                        else
                        {
                            // Scroll to follow the motion ev
                            pointerIndex = ev.FindPointerIndex(_activePointerId);
                            var x = ev.GetX(pointerIndex);

                            if (Math.Abs(x - _downMotionX) > _scaledTouchSlop)
                            {
                                Pressed = true;
                                Invalidate();
                                OnStartTrackingTouch();
                                TrackTouchEvent(ev, StepValueContinuously);
                                AttemptClaimDrag();
                            }
                        }

                        if (NotifyWhileDragging)
                        {
                            if (_pressedThumb == Thumb.Lower)
                                OnLowerValueChanged();
                            if (_pressedThumb == Thumb.Upper)
                                OnUpperValueChanged();
                        }
                    }
                    break;
                case MotionEventActions.Up:
                    if (_isDragging)
                    {
                        TrackTouchEvent(ev, true);
                        OnStopTrackingTouch();
                        Pressed = false;
                    }
                    else
                    {
                        // Touch up when we never crossed the touch slop threshold
                        // should be interpreted as a tap-seek to that location.
                        OnStartTrackingTouch();
                        TrackTouchEvent(ev, true);
                        OnStopTrackingTouch();
                    }
                    if (_pressedThumb == Thumb.Lower)
                        OnLowerValueChanged();
                    if (_pressedThumb == Thumb.Upper)
                        OnUpperValueChanged();
                    _pressedThumb = null;
                    Invalidate();
                    break;
                case MotionEventActions.PointerDown:
                    var index = ev.PointerCount - 1;
                    // readonly int index = ev.getActionIndex();
                    _downMotionX = ev.GetX(index);
                    _activePointerId = ev.GetPointerId(index);
                    Invalidate();
                    break;
                case MotionEventActions.PointerUp:
                    OnSecondaryPointerUp(ev);
                    Invalidate();
                    break;
                case MotionEventActions.Cancel:
                    if (_isDragging)
                    {
                        OnStopTrackingTouch();
                        Pressed = false;
                    }
                    Invalidate(); // see above explanation
                    break;
            }
            return true;
        }

        private void OnSecondaryPointerUp(MotionEvent ev)
        {
            var pointerIndex = (int)(ev.Action & MotionEventActions.PointerIndexMask) >>
                               (int)MotionEventActions.PointerIndexShift;

            var pointerId = ev.GetPointerId(pointerIndex);
            if (pointerId == _activePointerId)
            {
                // This was our active pointer going up. Choose
                // a new active pointer and adjust accordingly.
                // TODO: Make this decision more intelligent.
                var newPointerIndex = pointerIndex == 0 ? 1 : 0;
                _downMotionX = ev.GetX(newPointerIndex);
                _activePointerId = ev.GetPointerId(newPointerIndex);
            }
        }

        private void TrackTouchEvent(MotionEvent ev, bool step)
        {
            var pointerIndex = ev.FindPointerIndex(_activePointerId);
            var x = ev.GetX(pointerIndex);

            if (Thumb.Lower.Equals(_pressedThumb) && !MinThumbHidden)
            {
                SetNormalizedMinValue(ScreenToNormalized(x), step, true);
            }
            else if (Thumb.Upper.Equals(_pressedThumb) && !MaxThumbHidden)
            {
                SetNormalizedMaxValue(ScreenToNormalized(x), step, true);
            }
        }

        /// <summary>
        ///     Tries to claim the user's drag motion, and requests disallowing any ancestors from stealing evs in the drag.
        /// </summary>
        private void AttemptClaimDrag()
        {
            Parent?.RequestDisallowInterceptTouchEvent(true);
        }

        /// <summary>
        ///     This is called when the user has started touching this widget.
        /// </summary>
        private void OnStartTrackingTouch()
        {
            _isDragging = true;
            DragStarted?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        ///     This is called when the user either releases his touch or the touch is canceled.
        /// </summary>
        private void OnStopTrackingTouch()
        {
            _isDragging = false;
            DragCompleted?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        ///     Ensures correct size of the widget.
        /// </summary>
        protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            var width = 200;
            if (MeasureSpecMode.Unspecified != MeasureSpec.GetMode(widthMeasureSpec))
            {
                width = MeasureSpec.GetSize(widthMeasureSpec);
            }

            var height = ThumbImage.Height
                         + (ShowTextAboveThumbs ? PixelUtil.DpToPx(Context, HeightInDp) + PixelUtil.SpToPx(Context, TextSizeInSp) : 0)
                         + (ThumbShadow ? ThumbShadowYOffset + _thumbShadowBlur : 0);
            if (MeasureSpecMode.Unspecified != MeasureSpec.GetMode(heightMeasureSpec))
            {
                height = Math.Min(height, MeasureSpec.GetSize(heightMeasureSpec));
            }
            SetMeasuredDimension(width, height);
        }

        /// <summary>
        ///     Draws the widget on the given canvas.
        /// </summary>
        protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);

            _paint.TextSize = _textSize;
            _paint.SetStyle(Paint.Style.Fill);
            _paint.Color = DefaultColor;
            _paint.AntiAlias = true;
            float minMaxLabelSize = 0;

            if (ShowLabels)
            {
                // draw min and max labels
                var minLabel = Context.GetString(Resource.String.demo_min_label);
                var maxLabel = Context.GetString(Resource.String.demo_max_label);
                minMaxLabelSize = Math.Max(_paint.MeasureText(minLabel), _paint.MeasureText(maxLabel));
                var minMaxHeight = _textOffset + _thumbHalfHeight + (float)_textSize / 3;
                canvas.DrawText(minLabel, 0, minMaxHeight, _paint);
                canvas.DrawText(maxLabel, Width - minMaxLabelSize, minMaxHeight, _paint);
            }
            _padding = _internalPad + minMaxLabelSize + _thumbHalfWidth;

            // draw seek bar background line
            _rect.Left = _padding;
            _rect.Right = Width - _padding;
            canvas.DrawRect(_rect, _paint);

            var selectedValuesAreDefault = NormalizedMinValue <= MinDeltaForDefault &&
                                           NormalizedMaxValue >= 1 - MinDeltaForDefault;

            var colorToUseForButtonsAndHighlightedLine =
                !Enabled || (!AlwaysActive && !ActivateOnDefaultValues && selectedValuesAreDefault)
                    ? DefaultColor // default values
                    : ActiveColor; // non default, filter is active

            // draw seek bar active range line
            _rect.Left = NormalizedToScreen(NormalizedMinValue);
            _rect.Right = NormalizedToScreen(NormalizedMaxValue);

            _paint.Color = colorToUseForButtonsAndHighlightedLine;
            canvas.DrawRect(_rect, _paint);

            // draw minimum thumb (& shadow if requested) if not a single thumb control
            if (!MinThumbHidden)
            {
                if (ThumbShadow)
                {
                    DrawThumbShadow(NormalizedToScreen(NormalizedMinValue), canvas);
                }
                DrawThumb(NormalizedToScreen(NormalizedMinValue), Thumb.Lower.Equals(_pressedThumb), canvas,
                    selectedValuesAreDefault);
            }

            // draw maximum thumb & shadow (if necessary)
            if (!MaxThumbHidden)
            {
                if (ThumbShadow)
                {
                    DrawThumbShadow(NormalizedToScreen(NormalizedMaxValue), canvas);
                }
                DrawThumb(NormalizedToScreen(NormalizedMaxValue), Thumb.Upper.Equals(_pressedThumb), canvas,
                    selectedValuesAreDefault);
            }

            // draw the text if sliders have moved from default edges
            if (!ShowTextAboveThumbs || (!ActivateOnDefaultValues && selectedValuesAreDefault))
                return;

            _paint.TextSize = _textSize;
            _paint.Color = TextAboveThumbsColor;

            var minText = ValueToString(GetSelectedMinValue(), Thumb.Lower);
            var maxText = ValueToString(GetSelectedMaxValue(), Thumb.Upper);
            var minTextWidth = _paint.MeasureText(minText);
            var maxTextWidth = _paint.MeasureText(maxText);
            // keep the position so that the labels don't get cut off
            var minPosition = Math.Max(0f, NormalizedToScreen(NormalizedMinValue) - minTextWidth * 0.5f);
            var maxPosition = Math.Min(Width - maxTextWidth,
                NormalizedToScreen(NormalizedMaxValue) - maxTextWidth * 0.5f);

            if (!MaxThumbHidden && !MinThumbHidden)
            {
                // check if the labels overlap, or are too close to each other
                var spacing = PixelUtil.DpToPx(Context, TextLateralPaddingInDp);
                var overlap = minPosition + minTextWidth - maxPosition + spacing;
                if (overlap > 0f)
                {
                    // we could move them the same ("overlap * 0.5f")
                    // but we rather move more the one which is farther from the ends, as it has more space
                    minPosition -= overlap * NormalizedMinValue / (NormalizedMinValue + 1 - NormalizedMaxValue);
                    maxPosition += overlap * (1 - NormalizedMaxValue) / (NormalizedMinValue + 1 - NormalizedMaxValue);
                }
                canvas.DrawText(minText,
                    minPosition,
                    _distanceToTop + _textSize,
                    _paint);
            }

            canvas.DrawText(maxText,
                maxPosition,
                _distanceToTop + _textSize,
                _paint);
        }

        protected string ValueToString(float value, Thumb thumb)
        {
            var func = FormatLabel;
            return func == null
                ? value.ToString(_textFormat, CultureInfo.InvariantCulture)
                : func(thumb, value);
        }

        /// <summary>
        ///     Overridden to save instance state when device orientation changes. This method is called automatically if you
        ///     assign an id to the RangeSliderControl widget using the Id. Other members of this class than the normalized min and
        ///     max values don't need to be saved.
        /// </summary>
        protected override IParcelable OnSaveInstanceState()
        {
            var bundle = new Bundle();
            bundle.PutParcelable("SUPER", base.OnSaveInstanceState());
            bundle.PutDouble("MIN", NormalizedMinValue);
            bundle.PutDouble("MAX", NormalizedMaxValue);
            return bundle;
        }

        /// <summary>
        ///     Overridden to restore instance state when device orientation changes. This method is called automatically if you
        ///     assign an id to the RangeSliderControl widget using the {@link #setId(int)} method.
        /// </summary>
        protected override void OnRestoreInstanceState(IParcelable parcel)
        {
            var bundle = (Bundle)parcel;
            base.OnRestoreInstanceState((IParcelable)bundle.GetParcelable("SUPER"));
            NormalizedMinValue = bundle.GetFloat("MIN");
            NormalizedMaxValue = bundle.GetFloat("MAX");
        }

        /// <summary>
        ///     Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
        /// </summary>
        /// <param name="screenCoord">The x-coordinate in screen space where to draw the image.</param>
        /// <param name="pressed">Is the thumb currently in "pressed" state?</param>
        /// <param name="canvas">The canvas to draw upon.</param>
        /// <param name="areSelectedValuesDefault"></param>
        private void DrawThumb(float screenCoord, bool pressed, Canvas canvas, bool areSelectedValuesDefault)
        {
            Bitmap buttonToDraw;
            if (!Enabled || (!ActivateOnDefaultValues && areSelectedValuesDefault))
                buttonToDraw = ThumbDisabledImage;
            else
                buttonToDraw = pressed ? ThumbPressedImage : ThumbImage;

            canvas.DrawBitmap(buttonToDraw, screenCoord - _thumbHalfWidth, _textOffset, _paint);
        }

        /// <summary>
        ///     Draws a drop shadow beneath the slider thumb.
        /// </summary>
        /// <param name="screenCoord">the x-coordinate of the slider thumb</param>
        /// <param name="canvas">the canvas on which to draw the shadow</param>
        private void DrawThumbShadow(float screenCoord, Canvas canvas)
        {
            _thumbShadowMatrix.SetTranslate(screenCoord + ThumbShadowXOffset,
                _textOffset + _thumbHalfHeight + ThumbShadowYOffset);
            _translatedThumbShadowPath.Set(_thumbShadowPath);
            _translatedThumbShadowPath.Transform(_thumbShadowMatrix);
            canvas.DrawPath(_translatedThumbShadowPath, _shadowPaint);
        }

        /// <summary>
        /// Decides which (if any) thumb is touched by the given x-coordinate.
        /// </summary>
        /// <param name="touchX">The x-coordinate of a touch ev in screen space.</param>
        /// <returns>The pressed thumb or null if none has been touched.</returns>
        private Thumb? EvalPressedThumb(float touchX)
        {
            Thumb? result = null;
            var minThumbPressed = IsInThumbRange(touchX, NormalizedMinValue);
            var maxThumbPressed = IsInThumbRange(touchX, NormalizedMaxValue);
            if (minThumbPressed && maxThumbPressed)
                // if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
                result = touchX / Width > 0.5f ? Thumb.Lower : Thumb.Upper;
            else if (minThumbPressed)
                result = Thumb.Lower;
            else if (maxThumbPressed)
                result = Thumb.Upper;
            return result;
        }

        /// <summary>
        ///     Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb
        ///     x-coordinate.
        /// </summary>
        /// <param name="touchX">The x-coordinate in screen space to check.</param>
        /// <param name="normalizedThumbValue">The normalized x-coordinate of the thumb to check.</param>
        /// <returns>true if x-coordinate is in thumb range, false otherwise.</returns>
        private bool IsInThumbRange(float touchX, float normalizedThumbValue)
        {
            return Math.Abs(touchX - NormalizedToScreen(normalizedThumbValue)) <= _thumbHalfWidth;
        }

        /// <summary>
        /// Sets normalized min value to value so that 0 <= value <= normalized max value <= 1. The View will get Invalidated when calling this method.
        /// </summary>
        /// <param name="value">The new normalized min value to set.</param>
        /// <param name="step">If true then value is rounded to <see cref="StepValue"/></param>
        /// <param name="checkValue">If true check if value falls inside min/max</param>
        private void SetNormalizedMinValue(float value, bool step, bool checkValue)
        {
            NormalizedMinValue = checkValue
                                    ? Math.Max(0f, Math.Min(1f, Math.Min(value, NormalizedMaxValue)))
                                    : value;
            if (step)
                NormalizedMinValue = ValueToNormalized(NormalizedToValue(NormalizedMinValue));
            Invalidate();
        }

        /// <summary>
        /// Sets normalized max value to value so that 0 <= normalized min value <= value <= 1. The View will get Invalidated when calling this method.
        /// </summary>
        /// <param name="value">The new normalized max value to set.</param>
        /// <param name="step">If true then value is rounded to <see cref="StepValue"/></param>
        /// <param name="checkValue">If true check if value falls inside min/max</param>
        private void SetNormalizedMaxValue(float value, bool step, bool checkValue)
        {
            NormalizedMaxValue = checkValue
                                    ? Math.Max(0f, Math.Min(1f, Math.Max(value, NormalizedMinValue)))
                                    : value;
            if (step)
                NormalizedMaxValue = ValueToNormalized(NormalizedToValue(NormalizedMaxValue));
            Invalidate();
        }

        /// <summary>
        ///     Converts a normalized value to a Number object in the value space between absolute minimum and maximum.
        /// </summary>
        protected float NormalizedToValue(float normalized)
        {
            var v = AbsoluteMinValue + normalized * MinToMaxRange;

            // TODO parameterize this rounding to allow variable decimal points
            if (Math.Abs(StepValue) < float.Epsilon)
                return (float)Math.Round(v * 100) / 100f;
            var normalizedToValue = (float)Math.Round(v / StepValue) * StepValue;
            return Math.Max(AbsoluteMinValue, Math.Min(AbsoluteMaxValue, normalizedToValue));
        }

        /// <summary>
        /// Converts the given Number value to a normalized float.
        /// </summary>
        /// <param name="value">The Number value to normalize.</param>
        /// <returns>The normalized float.</returns>
        protected float ValueToNormalized(float value)
        {
            if (Math.Abs(MinToMaxRange) < float.Epsilon)
            {
                // prev division by zero, simply return 0.
                return 0f;
            }
            return (value - AbsoluteMinValue) / MinToMaxRange;
        }

        private void UpdateTextOffset()
        {
            _textOffset = _showTextAboveThumbs
                ? _textSize + PixelUtil.DpToPx(Context, DefaultTextDistanceToButtonInDp) + _distanceToTop
                : 0;
        }


        /// <summary>
        /// Converts a normalized value into screen space.
        /// </summary>
        /// <param name="normalizedCoord">The normalized value to convert.</param>
        /// <returns>The converted value in screen space.</returns>
        private float NormalizedToScreen(float normalizedCoord)
        {
            return _padding + normalizedCoord * (Width - 2 * _padding);
        }

        /// <summary>
        /// Converts screen space x-coordinates into normalized values.
        /// </summary>
        /// <param name="screenCoord">The x-coordinate in screen space to convert.</param>
        /// <returns>The normalized value.</returns>
        private float ScreenToNormalized(float screenCoord)
        {
            var width = Width;
            if (width <= 2 * _padding)
            {
                // prev division by zero, simply return 0.
                return 0f;
            }
            var result = (screenCoord - _padding) / (width - 2 * _padding);
            return Math.Min(1f, Math.Max(0f, result));
        }

        public event EventHandler LowerValueChanged;
        public event EventHandler UpperValueChanged;
        public event EventHandler DragStarted;
        public event EventHandler DragCompleted;

        protected virtual void OnLowerValueChanged()
        {
            LowerValueChanged?.Invoke(this, EventArgs.Empty);
        }

        protected virtual void OnUpperValueChanged()
        {
            UpperValueChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}