knob control in xamarin forms

you can create knob control by following steps








Step 1 : 


Add the following class in portable project :


    public class Knob : View
    {
        public static readonly BindableProperty MinProperty = BindableProperty.Create<Knob,float> (p => p.Min, 0);
        public static readonly BindableProperty MaxProperty = BindableProperty.Create<Knob,float> (p => p.Max, 100);
        public static readonly BindableProperty CurrentProperty = BindableProperty.Create<Knob,float> (p => p.Current, 50);
        public static readonly BindableProperty BoundsProperty = BindableProperty.Create<Knob,Rectangle> (p => p.Bounds, new Rectangle(0,0,300,300));
        public static readonly BindableProperty ShowTouchPathProperty = BindableProperty.Create<Knob,bool> (p => p.ShowTouchPath, false);

        public float Min {
            get { return (float) GetValue(MinProperty); }
            set { SetValue (MinProperty, value); OnPropertyChanged(); }
        }

        public float Max {
            get { return (float) GetValue(MaxProperty); }
            set { SetValue (MaxProperty, value); OnPropertyChanged(); }
        }

        public float Current {
            get { return (float) GetValue(CurrentProperty); }
            set { SetValue (CurrentProperty, value); OnPropertyChanged();}
        }

public new Rectangle Bounds {
get { return (Rectangle)GetValue(BoundsProperty); }
set { SetValue(BoundsProperty, value); }
}

public bool ShowTouchPath {
get { return (bool)GetValue(ShowTouchPathProperty); }
set { SetValue(ShowTouchPathProperty, value); }
}
         
public bool IsRotationEndedRegistered { get { return RotationEnded != null; }}

public Action<float> RotationStarted;
public Action<float> RotationEnded;

public Knob(Rectangle frame, float min, float max, float current, bool showTouchPath = false)
{
SetValue (MinProperty, min);
SetValue (MaxProperty, max);
SetValue (CurrentProperty, current);
SetValue (BoundsProperty, frame);
SetValue (ShowTouchPathProperty, showTouchPath);
}

public Knob(Rectangle frame)
{
Bounds = frame;
}
    }


Step 2 :


Add render class in android & IOSproject :

1- android :

using System;
using Xamarin.Forms;
using KnobControl;
using KnobControl.Droid;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using System.ComponentModel;

[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]

namespace KnobControl.Droid
{
    public class KnobRenderer: ViewRenderer<Knob, RoundKnobButton>
    {
RoundKnobButton knob;
static Bitmap bmpRotorOn, bmpRotorOff;

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

if (e.OldElement != null || this.Element == null)
return;

var context = Forms.Context;
var metrics = Resources.DisplayMetrics;
var w = metrics.HeightPixels / 2 - 20;
var h = metrics.HeightPixels / 2 - 20;

// Load pictures once
if (bmpRotorOn == null || bmpRotorOff == null)
{
Console.WriteLine ("OnElementChanged :: Create Bitmaps");

Bitmap srcon = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);

float scaleWidth = ((float)w) / srcon.Width;
float scaleHeight = ((float)h) / srcon.Height;

Matrix matrix = new Matrix ();
matrix.PostScale (scaleWidth, scaleHeight);

bmpRotorOn = Bitmap.CreateBitmap (srcon, 0, 0, srcon.Width, srcon.Height, matrix, true);

srcon.Recycle ();

Bitmap srcoff = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);

bmpRotorOff = Bitmap.CreateBitmap (srcoff, 0, 0, srcoff.Width, srcoff.Height, matrix, true);

srcoff.Recycle ();
}

Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Width = " + Element.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Height = " + Element.Height);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Width = " + Element.Bounds.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Height = " + Element.Bounds.Height);

knob = new RoundKnobButton(context, Resource.Drawable.knob_bg, bmpRotorOn, bmpRotorOff, w, h);
knob.ShowTouchPath = Element.ShowTouchPath;
knob.MaxValue = Element.Max;
knob.MinValue = Element.Min;
knob.CurrentValue = Element.Current;

knob.RotationEnded += (float obj) =>
{
Console.WriteLine( "knob.RotationEnded with {0} ", obj );

if (Element != null && Element.RotationEnded != null)
{
Element.RotationEnded((float)obj);
}
};

knob.IsEnabled = Element.IsEnabled;

knob.OnRotate += ((float obj) =>
{
Console.WriteLine("percentage = {0}", obj);
});

SetNativeControl(knob);
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

if (Control == null || Element == null) return;

if (e.PropertyName == Knob.MaxProperty.PropertyName)
{
Control.MaxValue = Element.Max;
}
else if (e.PropertyName == Knob.MinProperty.PropertyName)
{
Control.MinValue = Element.Min;
}
else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
{
Control.CurrentValue = Element.Current;
}
else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
{
// var frame = new Rect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
// Control.Bounds = frame;
}
else if (e.PropertyName == Knob.IsEnabledProperty.PropertyName)
{
Control.IsEnabled = Element.IsEnabled;
}
}
    }
}


2 - IOS :

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using KnobControl;
using KnobControl.iOS;
using CoreGraphics;
using UIKit;

[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]

namespace KnobControl.iOS
{
    public class KnobRenderer : ViewRenderer<Knob, UIKnobControl>
    {
        public KnobRenderer()
        {
        }

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

            if (e.OldElement != null || this.Element == null)
                return;

            Console.WriteLine("OnElementChanged :: Element.Width" + Element.Width);
            Console.WriteLine("OnElementChanged :: Element.Height" + Element.Height);
            Console.WriteLine("OnElementChanged :: Element.Bounds.Width" + Element.Bounds.Width);
            Console.WriteLine("OnElementChanged :: Element.Bounds.Height" + Element.Bounds.Height);

            var mainScreen = UIScreen.MainScreen.Bounds;

            var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)mainScreen.Height/2-20, (nfloat)mainScreen.Height/2-20);
            var knob = new UIKnobControl(frame, Element.Min, Element.Max, Element.Current, Element.ShowTouchPath);

            knob.RotationEnded += (nfloat obj) =>
            {
                if(Element != null)
                {
                    Element.Current = (float)obj;
                }
            };

            SetNativeControl(knob);
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (Control == null || Element == null)
                return;

            if (e.PropertyName == Knob.MaxProperty.PropertyName)
            {
                Control.MaxValue = Element.Max;
            }
            else if (e.PropertyName == Knob.MinProperty.PropertyName)
            {
                Control.MinValue = Element.Min;
            }
            else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
            {
                Control.CurrentValue = Element.Current;
            }
            else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
            {
                var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
                Control.Bounds = frame;
            }
        }
    }
}

Usage :

     MainPage = new ContentPage
            {
                Padding = 0,
                    BackgroundImage = Device.OnPlatform("view_bg.png", "view_bg.png", "view_bg.png"),
                Content = new StackLayout
                {
                    Padding = 0,
                    VerticalOptions = LayoutOptions.CenterAndExpand,
                    HorizontalOptions = LayoutOptions.CenterAndExpand,
                    Children =
                    {
                        new Knob(rectangle, 0, 100, 25),
                        new Knob(rectangle, 0, 100, 75, true)
                    }
                }
            };


** you can find sample at :

https://github.com/corneliu-serediuc/Xamarin.Forms.KnobControl


Comments

  1. when i touch the knob control it doesn't move to current position.but when i drag the knob it moves to current position, how to resolve this
    ?

    ReplyDelete

Post a Comment

Popular posts from this blog

ScrollView in xamarin forms

Checkbox and RadioButon in xamarin forms

Navigation in Xamarin.Forms