Пользовательский TimePickerRenderer не запускает события для обновления TimeProperty - PullRequest
0 голосов
/ 31 января 2019

Я создал пользовательский 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-репозиторий с кодом, может быть, легко разобраться в моей проблеме.https://github.com/aismaniotto/Nullable24hTimePicker

1 Ответ

0 голосов
/ 25 марта 2019

Спасибо за вашу помощь.После более глубокого изучения кода и отладки я понимаю, что проблема заключалась в реализации OkButton.В Android 8, по-видимому, обратный вызов из TimePickerDialog вызывался до того, как реализация OkButton и то, что там было, игнорируется.В более старой версии реализация OKButton была вызвана раньше и по какой-то причине отменяет вызов обратного вызова.

Таким образом, удалив реализацию OkButton, проблема была решена ... если вспомнить, что на рабочей версии все равно игнорировалось.В конце концов, я обязуюсь зарегистрироваться в репозитории.

Большое спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...