Добрый день, мы разрабатываем приложение 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 и создать к нему свой презентер.