时间选择器(以秒为单位) [英] Timepicker with seconds

查看:98
本文介绍了时间选择器(以秒为单位)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Xamarin Forms,并且在我的应用程序中的某个位置,我需要用户能够以HH:mm:ss格式输入时间.因此,基本上我需要一个这样的控件:

I am using Xamarin Forms and somewhere in my application, I need the user to be able to enter a time in the format HH:mm:ss. So, basically I need a control like this one:

通过使用自定义的iOS渲染,我已经能够从TimePicker中删除AM/PM部分,以实现以下目的:

By using a custom iOS render, I've been able to remove the AM/PM part from the TimePicker to achieve something like this:

这是我的渲染器代码:

public class TimePickerSecondsRenderer : TimePickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
    {
        base.OnElementChanged(e);

        var timePicker = (UIDatePicker)Control.InputView;
        timePicker.Locale = new NSLocale("CA");

    }
}

我认为这是朝正确方向迈出的一小步,但是当我为秒添加第三列以及每一列的标签时,我真的很茫然.

It's a small step in the right direction I suppose, but I'm really at loss when it comes to adding a third column for the seconds, as well as the labels for each column.

我看了这篇帖子,但是它到目前为止对我没有真正的帮助.

I took a look at this post but it doesn't really help me so far.

有人通过他们的Xamarin Forms项目设法实现了这种控制吗?您愿意分享一些指针吗?

Did anyone managed to achieve that kind of control with their Xamarin Forms project? Would you mind sharing some pointers?

推荐答案

我终于有了一个可行的实现.结果如下:

I finally got a working implementation. Here's the result:

唯一的缺点是标签"hr","min"和"sec"被标记为"hr","min"和"sec".在自己的专栏中.这样,如果用户尝试与他们互动,就会有反弹的效果.稍后我会尝试改善它. 欢迎提出建议!

当然还有代码(如果某人可以使用的话):

And, of course, the code (if it can be of use to someone):

渲染器

[assembly: ExportRendererAttribute(typeof(MyTimePicker), typeof(MyTimePickerRenderer))]
namespace MyProject.iOS.Renderers
{
    public class MyTimePickerRenderer : PickerRenderer
    {
        internal static  IDevice Device;

        internal const int ComponentCount = 6;

        private const int _labelSize = 30;

        private MyTimePicker _myTimePicker;

        public MyTimePickerRenderer()
        {
            // This is dependent on XForms (see Update note)
            Device = Resolver.Resolve<IDevice>();
        }

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

            if (Control != null)
            {
                Control.BorderStyle = UITextBorderStyle.None;

                _myTimePicker = e.NewElement as MyTimePicker;

                var customModelPickerView = new UIPickerView
                    {
                        Model = new MyTimePickerView(_myTimePicker)
                    };

                SelectPickerValue(customModelPickerView, _myTimePicker);
            
                CreatePickerLabels(customModelPickerView);

                Control.InputView = customModelPickerView;
            }
        }

    private void SelectPickerValue(UIPickerView customModelPickerView, MyTimePicker myTimePicker)
    { 
        customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Hours), 0, false);
        customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Minutes), 2, false);
        customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Seconds), 4, false);
    }

    private void CreatePickerLabels(UIPickerView customModelPickerView)
    {
        nfloat verticalPosition = (customModelPickerView.Frame.Size.Height / 2) - (_labelSize / 2);
        nfloat componentWidth = new nfloat(Device.Display.Width / ComponentCount / Device.Display.Scale);

        var hoursLabel = new UILabel(new CGRect(componentWidth, verticalPosition, _labelSize, _labelSize));
        hoursLabel.Text = "hrs";

        var minutesLabel = new UILabel(new CGRect((componentWidth * 3) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
        minutesLabel.Text = "mins";

        var secondsLabel = new UILabel(new CGRect((componentWidth * 5) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
        secondsLabel.Text = "secs";

        customModelPickerView.AddSubview(hoursLabel);
        customModelPickerView.AddSubview(minutesLabel);
        customModelPickerView.AddSubview(secondsLabel);
    }

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

        if (Control == null)
        {
            return;
        }

        if (e.PropertyName == "SelectedIndex")
        {
            var customModelPickerView = (UIPickerView)Control.InputView;

            SelectPickerValue(customModelPickerView, _myTimePicker);
        }
    }
    
    public class MyTimePickerView : UIPickerViewModel
    {
        private readonly MyTimePicker _myTimePicker;

        public MyTimePickerView(MyTimePicker picker)
        {
            _myTimePicker = picker;
        }

        public override nint GetComponentCount(UIPickerView pickerView)
        {
            return new nint(MyTimePickerRenderer.ComponentCount);
        }

        public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
        {
            if (component == 0)
            {
                // Hours
                return new nint(24);
            }

            if (component % 2 != 0)
            {
                // Odd components are labels for hrs, mins and secs
                return new nint(1);
            }

            // Minutes & seconds
            return new nint(60);
        }

        public override string GetTitle(UIPickerView pickerView, nint row, nint component)
        {
            if (component == 0)
            {
                return row.ToString();
            }
            else if (component == 1)
            {
                return null;
            }
            else if (component == 3)
            {
                return null;
            }
            else if (component == 5)
            {
                return null;
            }
            return row.ToString("00");
        }

        public override void Selected(UIPickerView pickerView, nint row, nint component)
        {
            var selectedHours = pickerView.SelectedRowInComponent(0);
            var selectedMinutes = pickerView.SelectedRowInComponent(2);
            var selectedSeconds = pickerView.SelectedRowInComponent(4);

            var time = new TimeSpan((int)selectedHours, (int)selectedMinutes, (int)selectedSeconds);

            _myTimePicker.SelectedTime = time;
        }

        public override nfloat GetComponentWidth(UIPickerView pickerView, nint component)
        {
            var screenWidth = MyTimePickerRenderer.Device.Display.Width;

            var componentWidth = screenWidth /
                MyTimePickerRenderer.ComponentCount /
                MyTimePickerRenderer.Device.Display.Scale;
        
            return new nfloat(componentWidth);
        }
    }
}

自定义可绑定选择器类

public class MyTimePicker : Picker
{
    public static readonly BindableProperty SelectedTimeProperty =
    BindableProperty.Create<MyTimePicker, TimeSpan>(p => p.SelectedTime, TimeSpan.MinValue, BindingMode.TwoWay,propertyChanged: OnSelectedTimePropertyPropertyChanged);

    public MyTimePicker()
    {
        // Ugly hack since Xamarin Forms' Picker uses only one component internally
        // This is a list of all possible timespan from 0:00:00 to 23:59:59
        for (int hour = 0; hour < 24; hour++)
        {
            for (int minute = 0; minute < 60; minute ++)
            {
                for (int second = 0; second < 60; second++)
                {
                    Items.Add(string.Format("{0:D2}:{1:D2}:{2:D2}", hour, minute, second));
                }
            }
        }

        base.SelectedIndexChanged += (o, e) =>
        {
            if (base.SelectedIndex < 0)
            {
                SelectedTime = TimeSpan.MinValue;
                return;
            }

            int index = 0;
            foreach (var item in Items)
            {
                if (index == SelectedIndex)
                {
                    SelectedTime = TimeSpan.Parse(item);
                    break;
                }
                index++;
            }
        };
    }

    public TimeSpan SelectedTime
    {
        get { return (TimeSpan)GetValue(SelectedTimeProperty); }
        set { SetValue(SelectedTimeProperty, value); }
    }

    private static void OnSelectedTimePropertyPropertyChanged(BindableObject bindable, TimeSpan value, TimeSpan newValue)
    {
        var picker = (MyTimePicker)bindable;

        var itemMatch = picker.Items.FirstOrDefault(x => x == newValue.ToString());
        var index = picker.Items.IndexOf(itemMatch);

        picker.SelectedIndex = index;
    }
}

XAML用法

<myControls:MyTimePicker SelectedTime="{Binding SelectedTime}" />

其中SelectedTime是用于绑定的ViewModel属性.必须为TimeSpan类型.

Where SelectedTime is your ViewModel property used for binding. Must be of type TimeSpan.

我相信它可以改善,因此,如果您有任何建议,请置评.

I'm sure it can be improved, so comment away if you have suggestions.

我已经更新了渲染器代码.

I've updated the renderer code.

  • 小时",分钟"和秒"现在是标签,不再是它们自己的列.这意味着用户不再可以与他们互动(不再有弹跳效果)
  • 我还修复了一个错误,即如果从ViewModel中设置了选择器值,则在打开键盘时选择仍位于00:00:00
  • 更好地支持iPhone Plus分辨率

请注意,此代码取决于XForms( https://github.com/XLabs/Xamarin -Forms-Labs )来获取设备屏幕尺寸,因为这是我在项目中使用的框架,但是可以轻松对其进行修改.

Note that this code is dependent on XForms (https://github.com/XLabs/Xamarin-Forms-Labs) to get the device screen size, because that's the framework that I'm using for my project, but it can easily be modified.

这篇关于时间选择器(以秒为单位)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆