Задать вопрос
@VitCher

Миграция на MVVMcross, как сделать правильно?

Добрый день, мы разрабатываем приложение iMonitoring на самодельной MVVM архитектуре.
В общем ядре у нас есть модель класс ViewM, контроллер модели ViewControllerMC который имеет методы и события events. На эти события подписывается контроллер представления ViewControllerVC в котором уже происходит работа с UI каждой из платформ.
Т.е. схема такая ViewM <-> ViewControllerMC <---> ViewControllerVC.
ViewControllerVC на iOS платформе унаследован от UIViewController, а на Android это отдельный класс ViewControllerVC.
И вопрос миграции заключается именно в нем.
Основная идея состоит в том что ViewControllerVC как бы соостветсвует UIViewControllerVC может включать в себя дочерние ViewControllerVC. Android Activity и фрагменты выступают как контейнеры в которые вставляются корневые ViewControllerVC (точнее подконтрольные им вью - андроидовские View). Каждый ViewControllerVC работает с конкретной моделью и имеет свою подконтрольную View (RelativeLayout, FrameLayout, LinearLayout) с которой и копошится, слушая сигналы от модели.

Ниже я привожу этот класс.

public class ViewControllerVC : IViewAndroidController
    {
        private ViewControllerMC modelController;

        private NavigationItemVC navigationItemVC;

        private Activity activity;

        public ViewControllerVC(Activity activity)
        {
            IsDestroyed = false;
            this.activity = activity;
        }

        public ViewControllerVC(Activity activity, ViewControllerMC model, View view = null)
        {
            IsDestroyed = false;
            this.activity = activity;

            if (view == null)
            {
                // есть базовый алгоритм загрузки View, но его можно перекрыть
                view = LoadBaseView(model);
            }

            if (view != null)
            {
                SetControlledView(view, model.View);
                BindWithModel(model);
            }
        }

        public event EventHandler ViewWillDestroy;

        /// <summary>
        /// Если NavigationController == null то операции Push, Present реализуют запуск ВьюКонтроллера в новом Активити (ContentActivity.cs),
        /// если не null то обращаются к NavigationController чтобы он открыл вьюху и вьюКонтрллер в 
        /// своем окне поверх текщуего вьюКонтроллера через AddView(...).
        /// </summary>
        /// <value>Навгационный контроллер</value>
        public NavigationVC NavigationController { get; set; }

        public string ModelUUID { get; private set; }

        public Activity Activity
        { 
            get
            {
                return activity;
            }
        }

        public ViewVC View { get; private set; }

        public View BaseRootView
        { 
            get
            { 
                if (View != null)
                {
                    return View.View;
                }

                return null;
            }
        }

        protected ViewControllerMC ModelController
        {
            get
            {
                return GetModelController();
            }

            private set
            {
                SetModelController(value);
            }
        }

        // устанавливаем подконтрольную вьюху и потом ищем в ней нужные контролы
        public void SetControlledView(View view, ViewMC model)
        {
            View = new ViewVC(Activity, model, view);
        }

        public virtual void BindWithModel(ViewControllerMC model)
        {
            if (IsAliveModel)
            {
                UnbindFromModelController();
            }

            ModelController = model;
            ModelUUID = ModelController.UUID;

            FindViews();
            InitByModel();
            ApplyDefaultInterfaceSettings();
            LocalizeUserInterface();
            SubscribeToViewEvent();
            SubscribeToModelEvent();
        }
            
        public virtual void AddViewToParent(View parentView)
        {
            if (View == null || View.View == null)
            {
                return;
            }

            var currentParent = (ViewGroup)View.View.Parent;

            if (currentParent != null)
            {
                if (!ReferenceEquals(currentParent, parentView))
                {
                    currentParent.RemoveView(View.View);
                }
                else
                {
                    return;
                }
            }

            if (parentView is ViewGroup)
            {
                (parentView as ViewGroup).AddView(View.View, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
            }
        }

        public void HideSoftInput(IBinder binder)
        {
            if (!IsDestroyed)
            {
                var inputManager = (InputMethodManager)Activity.GetSystemService(Activity.InputMethodService);
                inputManager.HideSoftInputFromWindow(binder, 0);
            }
        }

        public void ShowSoftInput(View view, ShowFlags flags)
        {
            var inputManager = (InputMethodManager)Activity.GetSystemService(Activity.InputMethodService);
            inputManager.ShowSoftInput(view, flags);
        }

        public virtual void RemoveFromParent()
        {
            // здесь удаляется представление из родителя, андроид это позволяет.
            // а удаление из словаря родителя CompilanceControllers.Remove(controller.ModelController), если необходимо,
            // происходит по дополнительному запросу события от чилда AskRemoveControllerFromParent() в перекрытом этом методе
            // а также в перекрытом методе анимируем чилд относительно роидителя как хотим.
            // Можно не удалять а скрывать за экарн.

            // бывает ситуация когда вьюха уже уничтожена,
            // и после этого по событию окончания анимации срабатывает удаление вьюхи с экрана
            if (View == null || View.View == null)
            {
                return;
            }

            View.RemoveFromParentView();
        }

        protected virtual View LoadBaseView(ViewControllerMC model)
        {
            View view = null;
            var layoutId = ResourceHelper.GetIdByName(model.ViewControllerId);

            if (layoutId > 0)
            {
                view = activity.LayoutInflater.Inflate(layoutId, null);
            }

            return view;
        }

        protected virtual void InitByModel()
        {
            InitNavigationItem();
            PresentationStyle = WindowPresentationStyle.Center;
        }

        protected virtual void FindViews()
        {
        }

        protected virtual void SubscribeToModelEvent()
        {
            ModelController.WillDestroy += HandleWillDestroy;
            ModelController.InsetViewChanged += HandleInsetViewChanged;
            ModelController.FromParentViewControllerRemoved += HandleFromParentViewControllerRemoved;
            ModelController.ViewDestroyed += HandleModelControllerViewDestroyed;
            ModelController.Dismissed += HandleViewControllerDismissed;
            //// ModelController.IsEditingChanged += HandleModelControllerIsEditingChanged;

            ModelController.ViewOverlaid += HandleViewOverlaid;
            ModelController.ViewControllerPresented += HandleViewControllerPresented;
            ModelController.PopoverControllerChaned += HandlePopoverControllerChaned;
            //// ModelController.CultureChanged += HandleCultureChanged;
            ModelController.InSightChanged += HandleModelControllerInSightChanged;
        }
            
        protected virtual void UnsubscribeFromModelEvent()
        {
            ModelController.WillDestroy -= HandleWillDestroy;
            ModelController.InsetViewChanged -= HandleInsetViewChanged;
            ModelController.FromParentViewControllerRemoved -= HandleFromParentViewControllerRemoved;

            ModelController.ViewDestroyed -= HandleModelControllerViewDestroyed;
            ModelController.Dismissed -= HandleViewControllerDismissed;

            ModelController.ViewOverlaid -= HandleViewOverlaid;
            ModelController.ViewControllerPresented -= HandleViewControllerPresented;
            ModelController.PopoverControllerChaned -= HandlePopoverControllerChaned;
            ModelController.InSightChanged -= HandleModelControllerInSightChanged;
        }
            
        protected virtual void SubscribeToViewEvent()
        {
            View.View.ViewAttachedToWindow += HandleViewAttachedToWindow;
            View.View.ViewDetachedFromWindow += HandleViewDetachedFromWindow;
        }

        protected void SafetyInvokeOnMainThread(Action action)
        {
            if (IsDestroyed)
            {
                return;
            }

            if ((Activity != null) && (action != null))
            {
                Activity.RunOnUiThread(action);
            }
        }

        private void PresentViewController(ViewControllerMC model)
        {
            ViewControllerFactory.Instance.RunDialogContainerByModelSafety(App.Instance.ActivityInstance, model);
        }
    }
}


Собственно все приложение и построенно на работе с этими вью контроллерами. Скажу сразу что это достаточно удобно в Андроид. Каждый контрол или группу мы можем вынести в отдельный ViewControllerVC и логику работы с его вьюхами и делать с ним что захотим - презентовать в отдельном окне, открыть детальное окно, вернуться обратно. Т.е. вся работа очень похожа на работу с UIViewControllerVC в iOs, полностью абстрогируясь от Activity и Fragment, используя их только как навигационные Пэйджи - контейнеры. Сейчас мы решили попробовать перейти или точнее перенести все это на MVVMcross т.к. вроде как это проверенная архитектура, отлаженная и прочее.
Какие аналоги ViewControllerVC есть в MVVMcross в Abndroid? Может быть как то можно унаследовать ViewControllerVC от IMvxAndroidView и создать к нему свой презентер.
  • Вопрос задан
  • 580 просмотров
Подписаться 1 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 1
@VitCher Автор вопроса
Вообщем модель ViewControllerMc в нашей архитектуре это как я понимаю аналог MvxViewModel,
контроллером является для iOs MvxViewController а для андроид MvxActivity.
Но MvxActivty нам подойдет только для коренвой общей страницы, а вот все входящие внурь чилды - как их можно релизовать чтобы они тоже работали с моделями MvxViewModel. Как я уже говрил - можно ли добавить свой тип, унаследовать ViewControllerVC от IMvxAndroidView и создать к нему свой презентер.

Приведу еще пример для понимания нашей архитектуры. Есть например домашний экран и в нем мы добавляем заголовок. В коде это выглядит так:
public class HomeScreenVC : ViewControllerVC
    {
        public HomeScreenVC(Activity activity, ViewControllerMC model) : base(activity, model)
        {
        }

        public HomeScreenTopBarVC TopBar { get; set; }

        protected override void InitByModel()
        {
            base.InitByModel();

            TopBar = (HomeScreenTopBarVC)ViewControllerFactory.Instance.GetOrCreateViewControllerByModel(Activity, ModelController.TopBar, BaseRootView.FindViewById<LinearLayout>(Resource.Id.HomeTopBarLayout));
        }

...

}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы