Custom Vertical Seekbar height not being set correctly

I am trying to build an app which will need vertical sliders so I am buidling a custom SeekBar. I have managed to get a seekbar on screen which is correctly shown vertically but I can't find how to set the height dynamically so that it shows properly- at the moment it is sort of squished into one row of the Linear Layout.

I am very familiar with C# but Android is new to me so this may well be a basic error but I am getting so frustrated!

Here is my custom class:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

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

namespace Lightslides001
{
public class BCSeekBar : SeekBar
{


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

    public BCSeekBar(Context context)
        : base(context)
    {
    }

    public BCSeekBar(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {
    }

    public BCSeekBar(Context context, IAttributeSet attrs, int defStyle)
        : base(context, attrs, defStyle)
    {
    }

    private int _min = 0;
    public int Min { get { return _min; } set { _min = value; } }

    public override int Progress
    {
        get
        {
            return base.Progress + _min;
        }
        set
        {
            base.Progress = value;
        }
    }

    public override int Max
    {
        get
        {
            return base.Max + _min;
        }
        set
        {
            base.Max = value + _min;
        }
    }

    public override void Draw(Android.Graphics.Canvas canvas)
    {
        canvas.Rotate(90);
        canvas.Translate(0, -this.Width);
        base.OnDraw(canvas);
    }

    public object GetInputData()
    {
        return (Progress + _min).ToString(CultureInfo.InvariantCulture);
    }

    protected void Measure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.Measure(heightMeasureSpec, widthMeasureSpec);
        SetMeasuredDimension(MeasuredHeight, MeasuredWidth);
    }

    public void OnSizeChanged(int w, int h, int oldw, int oldh)
    {
        base.OnSizeChanged(h, w, oldh, oldw);
    }

    public bool OnTouchEvent(MotionEvent evt)
    {
        if (!Enabled)
            return false;

        switch (evt.Action)
        {
            case MotionEventActions.Down:
            case MotionEventActions.Move:
            case MotionEventActions.Up:
                {
                    Progress = ((int)(Max * evt.YPrecision / Height));
                    OnSizeChanged(Width, Height, 0, 0);
                }
                break;
            case MotionEventActions.Cancel:
                break;
        }

        return true;
    }
}


}

And here is my activity:

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

 namespace Lightslides001
 {
 [Activity(Label = "Lightslides001", 
    MainLauncher = true, 
    Icon = "@drawable/icon",
    ScreenOrientation=ScreenOrientation.Landscape)]

public class Activity1 : Activity
{
    int count = 1;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

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

        // Get our button from the layout resource,
        // and attach an event to it
        Button button = FindViewById<Button>(Resource.Id.MyButton);

        button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); };

        var seekbar = new BCSeekBar(this);
        seekbar.LayoutParameters = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WrapContent, LinearLayout.LayoutParams.FillParent);

        var ll = FindViewById<LinearLayout>(Resource.Id.layout1);

        ll.AddView(seekbar);

    }
}
 }

I really hope someone can help, thanks!

Posts

  • CheesebaronCheesebaron DKInsider, University mod

    Your OnMeasure, OnSizeChanged and OnTouchEvent methods hides the ones in SeekBar rather than overriding them.

    So the sinatures should be modified from:

    protected void Measure(int widthMeasureSpec, int heightMeasureSpec)
    public void OnSizeChanged(int w, int h, int oldw, int oldh)
    public bool OnTouchEvent(MotionEvent evt)
    

    to:

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
    public override bool OnTouchEvent(MotionEvent e)
    
  • EmilyChristyEmilyChristy GBMember ✭✭
    edited January 2013

    Thanks - yes I realised just after I posted that none of those methods were being called but I'm stumbling here too. The OnMeasure method is now ok with the override keyword but the OnSizeChanged method is proving more tricky. When I add the override keyword I get an error:

    Error   1   'Lightslides001.BCSeekBar.OnSizeChanged(int, int, int, int)': cannot change access modifiers when overriding 'protected' inherited member 'Android.Views.View.OnSizeChanged(int, int, int, int)'    C:\Users\Emily\documents\visual studio 2012\Projects\Lightslides001\Lightslides001\BCSeekBar.cs 94  30  Lightslides001
    

    If change override for 'new' it compiles. The seekbar is the wrong size but I've noticed some odd behaviour that may help someone figure out what is wrong. Also on screen I have a button. If I try and move the seekbar and then click the unrelated button the seekbar is redrawn at the right height!

    I am using a LinearLayout - is that relevant? This is my class now:

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    
    using Android.App;
    using Android.Content;
    using Android.OS;
    using Android.Util;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    
    namespace Lightslides001
    {
    public class BCSeekBar : SeekBar
    {
    
        /*private int oHeight = 320, oWidth = 29;
        private int oProgress = -1, oOffset = -1;
        private float xPos = -1, yPos = -1;
        private int top = -1, bottom = -1, left = -1, right = -1;
        */
    
        protected BCSeekBar(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }
    
        public BCSeekBar(Context context)
            : base(context)
        {
    
        }
    
        public BCSeekBar(Context context, IAttributeSet attrs)
            : base(context, attrs)
        {
        }
    
        public BCSeekBar(Context context, IAttributeSet attrs, int defStyle)
            : base(context, attrs, defStyle)
        {
        }
    
        private int _min = 0;
        public int Min { get { return _min; } set { _min = value; } }
    
        public override int Progress
        {
            get
            {
                return base.Progress + _min;
            }
            set
            {
                base.Progress = value;
            }
        }
    
        public override int Max
        {
            get
            {
                return base.Max + _min;
            }
            set
            {
                base.Max = value + _min;
            }
        }
    
        public override void Draw(Android.Graphics.Canvas canvas)
        {
            canvas.Rotate(90);
            canvas.Translate(0, -this.Width);
    
    
            base.OnDraw(canvas);
        }
    
        public object GetInputData()
        {
            return (Progress + _min).ToString(CultureInfo.InvariantCulture);
        }
    
        protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
            SetMeasuredDimension(MeasuredWidth, MeasuredHeight);
        }
    
        public new void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(h, w, oldh, oldw);
        }
    
        public override bool OnTouchEvent(MotionEvent evt)
        {
            if (!Enabled)
                return false;
    
            switch (evt.Action)
            {
                case MotionEventActions.Down:
                case MotionEventActions.Move:
                case MotionEventActions.Up:
                    {
                        Progress = ((int)(Max * evt.YPrecision / Height));
                        OnSizeChanged(Width, Height, 0, 0);
                    }
                    break;
                case MotionEventActions.Cancel:
                    break;
            }
    
            return true;
        }
    
    }
    }
    
  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    You don't want to use new, you want override. Using new is the same as not specifying either new or override, which will hide the base class member, and cause your method to not be invoked.

    Your source:

    public override void OnSizeChanged(int w, int h, int oldw, int oldh)
    

    The View.OnSizeChanged method:

    protected virtual void OnSizeChanged (int w, int h, int oldw, int oldh)
    

    Your error:

    cannot change access modifiers when overriding 'protected' inherited member 'Android.Views.View.OnSizeChanged(int, int, int, int)'
    

    Note that View.OnSizeChanged is protected, not public, hence the compiler error.

    The fix is to change the visibility to protected:

    partial class BCSeekBar {
        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(h, w, oldh, oldw);
        }
    }
    
  • CheesebaronCheesebaron DKInsider, University mod

    I've been fiddling with this Vertical SeekBar and got it to work pretty well, I've uploaded it to my GitHub: https://github.com/Cheesebaron/MonoDroid.VerticalSeekBar

  • EmilyChristyEmilyChristy GBMember ✭✭

    Thank you both so much, I've got it working!

  • Hello,
    I tried to use Cheesebaron for VerticalSeekBar.
    However, when I try to compile in debug it works, but release me from this error:

    Error: Duplicate managed type found! Mappings between managed types and Java types must be unique. First Type: 'MonoDroid.VerticalSeekbar.VerticalSeekBar, MonoDroid.VerticalSeekBar, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'; Second Type: 'MonoDroid.VerticalSeekbar.VerticalSeekBar, MonoDroid.VerticalSeekBar, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null '(MonoDroid.VerticalSeekBarTest)

    can you help me to make this fantastic example?

    thanks

    maurizio

  • CheesebaronCheesebaron DKInsider, University mod

    Should be fixed. It was due to both project having the same namespace.

  • Still does not work:

    I have:
    namespace: MonoDroid.VerticalSeekBarTest
    and
    namespace: MonoDroid.VerticalSeekbar

    I'm sorry but I'm a beginner and I still can not solve alone.

    maurizio

  • CheesebaronCheesebaron DKInsider, University mod

    I have pushed a new version to GitHub try that...

  • AlMansoriAlMansori USMember

    Hello,
    How would you keep the SeekBar thumb from rotating?
    IOW, I would like to keep the thumb graphic upright regardless whether my bar is horizontal in landscape or vertical in portrait layout.
    Peeking a the code I can see that rotating the canvas caused the thumb graphic to be rotated as well. I understand I could design to a separate rotated graphic just for the vertical bar, but was hoping if there is another way via code or xml?

    Thank you.

Sign In or Register to comment.