自定义TimePickerRenderer未触发事件以更新TimeProperty

问题描述 投票:0回答:1

我创建了一个自定义TimePicker和Android和iphone的渲染器,目标是允许它可以为空。作为灵感,使用了https://xamgirl.com/clearable-datepicker-in-xamarin-forms/

但是,由于某种原因,事件在设置时间时没有触发,这只发生在android中,更具体地说,在Android 8.1中。

在共享项目上:

public class NullableTimePicker : TimePicker
    {
        public NullableTimePicker()
        {
            Time = DateTime.Now.TimeOfDay;
            NullableTime = null;

            Format = @"HH\:mm";
        }
        public string _originalFormat = null;

        public static readonly BindableProperty PlaceHolderProperty =
            BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(NullableTimePicker), "  :  ");

        public string PlaceHolder
        {
            get { return (string)GetValue(PlaceHolderProperty); }
            set
            {
                SetValue(PlaceHolderProperty, value);
            }
        }


        public static readonly BindableProperty NullableTimeProperty =
        BindableProperty.Create(nameof(NullableTime), typeof(TimeSpan?), typeof(NullableTimePicker), null, defaultBindingMode: BindingMode.TwoWay);

        public TimeSpan? NullableTime
        {
            get { return (TimeSpan?)GetValue(NullableTimeProperty); }
            set { SetValue(NullableTimeProperty, value); UpdateTime(); }
        }

        private void UpdateTime()
        {
            if (NullableTime != null)
            {
                if (_originalFormat != null)
                {
                    Format = _originalFormat;
                }
            }
            else
            {
                Format = PlaceHolder;
            }

        }
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            if (BindingContext != null)
            {
                _originalFormat = Format;
                UpdateTime();
            }
        }

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            if (propertyName == TimeProperty.PropertyName ||
                (
                    propertyName == IsFocusedProperty.PropertyName &&
                    !IsFocused &&
                    (Time == DateTime.Now.TimeOfDay)))
            {
                AssignValue();
            }

            if (propertyName == NullableTimeProperty.PropertyName && NullableTime.HasValue)
            {
                Time = NullableTime.Value;
                if (Time == DateTime.Now.TimeOfDay)
                {
                    //this code was done because when date selected is the actual date the"DateProperty" does not raise  
                    UpdateTime();
                }
            }
        }

        public void CleanTime()
        {
            NullableTime = null;
            UpdateTime();
        }
        public void AssignValue()
        {
            NullableTime = Time;
            UpdateTime();

        }
    }

在Android项目上:

public class NullableTimePickerRenderer : ViewRenderer<NullableTimePicker, EditText>
    {
        public NullableTimePickerRenderer(Context context) : base(context)
        {
        }

        TimePickerDialog _dialog;
        protected override void OnElementChanged(ElementChangedEventArgs<NullableTimePicker> e)
        {
            base.OnElementChanged(e);

            this.SetNativeControl(new Android.Widget.EditText(Context));
            if (Control == null || e.NewElement == null)
                return;

            this.Control.Click += OnPickerClick;

            if (Element.NullableTime.HasValue)
                Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format);
            else
                this.Control.Text = Element.PlaceHolder;

            this.Control.KeyListener = null;
            this.Control.FocusChange += OnPickerFocusChange;
            this.Control.Enabled = Element.IsEnabled;

        }

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

            if (e.PropertyName == Xamarin.Forms.TimePicker.TimeProperty.PropertyName ||
                e.PropertyName == Xamarin.Forms.TimePicker.FormatProperty.PropertyName)
                SetTime(Element.Time);
        }

        void OnPickerFocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
        {
            if (e.HasFocus)
            {
                ShowTimePicker();
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (Control != null)
            {
                this.Control.Click -= OnPickerClick;
                this.Control.FocusChange -= OnPickerFocusChange;

                if (_dialog != null)
                {
                    _dialog.Hide();
                    _dialog.Dispose();
                    _dialog = null;
                }
            }

            base.Dispose(disposing);
        }

        void OnPickerClick(object sender, EventArgs e)
        {
            ShowTimePicker();
        }

        void SetTime(TimeSpan time)
        {
            Control.Text = DateTime.Today.Add(time).ToString(Element.Format);
            Element.Time = time;
        }

        private void ShowTimePicker()
        {
            CreateTimePickerDialog(this.Element.Time.Hours, this.Element.Time.Minutes);
            _dialog.Show();
        }

        void CreateTimePickerDialog(int hours, int minutes)
        {
            NullableTimePicker view = Element;
            _dialog = new TimePickerDialog(Context, (o, e) =>
            {
                view.Time = new TimeSpan(hours: e.HourOfDay, minutes: e.Minute, seconds: 0);
                view.AssignValue();
                ((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
                Control.ClearFocus();

                _dialog = null;
            }, hours, minutes, true);

            _dialog.SetButton("ok", (sender, e) =>
            {
                SetTime(Element.Time);
                this.Element.Format = this.Element._originalFormat;
                this.Element.AssignValue();
            });

            _dialog.SetButton2("clear", (sender, e) =>
            {
                this.Element.CleanTime();
                Control.Text = this.Element.Format;
            });
        }
    }

在iOS项目上:

public class NullableTimePickerRenderer : TimePickerRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
        {
            base.OnElementChanged(e);
            var timePicker = (UIDatePicker)Control.InputView;
            timePicker.Locale = new NSLocale("no_nb");

            if (e.NewElement != null && this.Control != null)
            {
                this.UpdateDoneButton();
                this.AddClearButton();
                this.Control.BorderStyle = UITextBorderStyle.Line;
                Control.Layer.BorderColor = UIColor.LightGray.CGColor;
                Control.Layer.BorderWidth = 1;

                if (Device.Idiom == TargetIdiom.Tablet)
                {
                    this.Control.Font = UIFont.SystemFontOfSize(25);
                }
            }

        }

        private void UpdateDoneButton()
        {
            var toolbar = (UIToolbar)Control.InputAccessoryView;
            var doneBtn = toolbar.Items[1];

            doneBtn.Clicked += (sender, args) =>
            {
                NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
                if (!baseTimePicker.NullableTime.HasValue)
                {
                    baseTimePicker.AssignValue();
                }
            };
        }

        private void AddClearButton()
        {
            var originalToolbar = this.Control.InputAccessoryView as UIToolbar;

            if (originalToolbar != null && originalToolbar.Items.Length <= 2)
            {
                var clearButton = new UIBarButtonItem("clear", UIBarButtonItemStyle.Plain, ((sender, ev) =>
                {
                    NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
                    this.Element.Unfocus();
                    this.Element.Time = DateTime.Now.TimeOfDay;
                    baseTimePicker.CleanTime();

                }));

                var newItems = new List<UIBarButtonItem>();
                foreach (var item in originalToolbar.Items)
                {
                    newItems.Add(item);
                }

                newItems.Insert(0, clearButton);

                originalToolbar.Items = newItems.ToArray();
                originalToolbar.SetNeedsDisplay();
            }
        }
    }

此代码适用于iOS和Android 8.1或更高版本,但在较低版本中,这不会触发事件设置时间,始终设置默认时间。

我也提供了代码的git repo,也许,让我很容易理解我的问题。 https://github.com/aismaniotto/Nullable24hTimePicker

c# mobile xamarin.forms xamarin.android
1个回答
0
投票

谢谢你的帮助。在进一步挖掘代码和调试之后,我意识到问题出现在OkButton实现上。在Android 8上,显然,在OkButton实现之前调用了TimePickerDialog的回调,那里被忽略了。在旧版本中,之前调用了OKButton实现,并且由于某种原因,取消了对回调的调用。

这样,删除OkButton实现,问题就解决了......记住在工作版本上,无论如何都被忽略了。最终,我将提交到存储库,进行注册。

非常感谢。

© www.soinside.com 2019 - 2024. All rights reserved.