MvvmCross: гибридное приложение Xamarin.Forms и Android Activity
Будучи новичком в MvvmCross, я решил создать небольшое приложение Xamarin.Forms. у меня есть MainPage.xaml
привязан к своей ViewModel MainViewModel.cs
который отображается первым. у меня есть FirstView.axml
находится в проекте дроида вместе с его активностью. Связанная ViewModel расположена в проекте Core рядом с MainViewModel и называется FirstViewModel.cs
После нажатия кнопки навигации я хочу, чтобы MvvmCross отображал FirstView.axml
верстка и привязка к ВМ. Тем не мение,
Всякий раз, когда команда вызывается, я получаю
03-10 10:11:38.704 D/ViewRootImpl(18964): ViewPostImeInputStage ACTION_DOWN
mvx:Diagnostic: 17,87 Showing ViewModel FirstViewModel
03-10 10:11:38.854 I/mono-stdout(18964): mvx:Diagnostic: 17,87 Showing ViewModel FirstViewModel
[0:] mvx:Diagnostic: 17,87 Showing ViewModel FirstViewModel
mvx:Error: 17,91 Failed to create ContentPage FirstPage
03-10 10:11:38.894 I/mono-stdout(18964): mvx:Error: 17,91 Failed to create ContentPage FirstPage
[0:] mvx:Error: 17,91 Failed to create ContentPage FirstPage
mvx:Error: 17,92 Skipping request for FirstViewModel
03-10 10:11:38.904 I/mono-stdout(18964): mvx:Error: 17,92 Skipping request for FirstViewModel
[0:] mvx:Error: 17,92 Skipping request for FirstViewModel`
На данный момент проект выглядит так:
Деятельность при запуске
[Activity(Label = "Hello MvvmCrossForms", MainLauncher = true)]
public class CrossFormsApp : FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//Init forms
Forms.Init(this, bundle);
InitialiseMvx();
//Create mvxformsApp
var mvxFormsApp = new MvxFormsApp();
var presenter = Mvx.Resolve<IMvxViewPresenter>() as MvxFormsDroidPagePresenter;
//Assign the viewPresenter
presenter.MvxFormsApp = mvxFormsApp;
LoadApplication(mvxFormsApp);
//Start mvxApp
Mvx.Resolve<IMvxAppStart>().Start();
}
private void InitialiseMvx()
{
if (MvxSingleton<IMvxIoCProvider>.Instance == null)
Mvx.RegisterSingleton(MvxSimpleIoCContainer.Initialize());
MvxAndroidSetupSingleton.EnsureSingletonAvailable(this.ApplicationContext)
.EnsureInitialized();
}
}
MainViewModel
public class MainViewModel : MvxViewModel
{
private string _inputString;
public ICommand NavigateCommand
{
get { return new MvxCommand(() => ShowViewModel<FirstViewModel>()); }
}
public string InputString
{
get { return _inputString; }
set { SetProperty(ref _inputString, value); }
}
}
По сути, я ищу обратное: MvvmCross: как перейти от обычного представления к модели просмотра Mvvm на Android?
2 ответа
В итоге я определил пустой интерфейс IXFViewModel
который я реализую на Forms
ViewModels. Затем в моем пользовательском докладчике я получаю список интерфейсов, которые MvxViewModelRequest.ViewModelType
реализует.
Если тип IXFViewModel
находится в списке интерфейсов, пользовательский ведущий - который расширяет MvxFormsDroidPagePresenter
- обрабатывает презентацию по телефону base.Show(request)
, Если IXFViewModel
тип не найден, пользовательский презентатор переключается на MvxAndroidViewPresenter
обрабатывать представление презентации.
Решение ниже:
начальная загрузка
public class Setup : MvxAndroidSetup
{
/// prefix ///
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var presenter = new XFDroidPresenter();
Mvx.RegisterSingleton<IMvxViewPresenter>(presenter);
return presenter;
}
}
Custom Presenter
public class XFDroidPresenter : MvxFormsDroidPagePresenter
{
protected MvxAndroidViewPresenter DroidPresenter { get; set; }
protected Activity Activity { get { return Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity; } }
public XFDroidPresenter()
{
DroidPresenter = new MvxAndroidViewPresenter();
}
public override void ChangePresentation(MvxPresentationHint hint)
{
if(Activity.GetType() != typeof(CrossFormsApp))
{
DroidPresenter.ChangePresentation(hint);
return;
}
base.ChangePresentation(hint);
}
public override void Show(MvxViewModelRequest request)
{
var implementedInterfaces = request.ViewModelType
.GetInterfaces()
.ToList();
if (!implementedInterfaces.Contains(typeof(IXFViewModel)))
{
DroidPresenter.Show(request);
return;
}
base.Show(request);
}
}
Launcher и Forms Activity
[Activity(Label = "Hello MvvmCrossForms", Icon = "@drawable/icon", MainLauncher = true)]
public class CrossFormsApp : FormsApplicationActivity
{
private MvxAndroidLifetimeMonitor _lifeTimeMonitor;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Forms.Init(this, bundle);
InitialiseMvx();
var mvxFormsApp = new MvxFormsApp();
var presenter = Mvx.Resolve<IMvxViewPresenter>() as MvxFormsDroidPagePresenter;
presenter.MvxFormsApp = mvxFormsApp;
LoadApplication(mvxFormsApp);
Mvx.Resolve<IMvxAppStart>().Start();
_lifeTimeMonitor = Mvx.Resolve<IMvxAndroidCurrentTopActivity>() as MvxAndroidLifetimeMonitor;
_lifeTimeMonitor.OnCreate(this);
}
private void InitialiseMvx()
{
if (MvxSingleton<IMvxIoCProvider>.Instance == null)
Mvx.RegisterSingleton(MvxSimpleIoCContainer.Initialize());
MvxAndroidSetupSingleton.EnsureSingletonAvailable(this.ApplicationContext)
.EnsureInitialized();
}
protected override void OnStart()
{
base.OnStart ();
_lifeTimeMonitor.OnStart(this);
}
protected override void OnRestart()
{
base.OnRestart ();
_lifeTimeMonitor.OnRestart(this);
}
protected override void OnResume()
{
base.OnResume ();
_lifeTimeMonitor.OnResume(this);
}
protected override void OnDestroy()
{
base.OnDestroy ();
_lifeTimeMonitor.OnDestroy(this);
}
}
Интерфейс и ViewModel
public interface IXFViewModel
{
}
public class MainViewModel : MvxViewModel, IXFViewModel
{
private string _inputString;
public ICommand NavigateCommand
{
get { return new MvxCommand(() => Close(this)); }
}
public string InputString
{
get { return _inputString; }
set { SetProperty(ref _inputString, value); }
}
}
В MvvmCross ViewPresenter является компонентом, который управляет навигацией между представлениями.
Таким образом, мы должны создать свой собственный
- что наследует от
MvxFormsDroidPagePresenter
потому что у нас есть приложение Forms перенаправляет не-форм звонки
ChangePresentation
(который обычно используется для Close())Show
в ViewPresenter, который обрабатывает деятельность (
MvxAndroidViewPresenter
)- список не-форм ViewModels (
_androidViews
)
class HybridDroidViewPresenter : MvxFormsDroidPagePresenter
{
private readonly MvxAndroidViewPresenter _androidPesenter;
private readonly List<Type> _androidViews;
protected Activity Activity => Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
public HybridDroidViewPresenter()
{
_androidPesenter = new MvxAndroidViewPresenter();
_androidViews = new List<Type>
{
typeof (FirstViewModel)
};
}
public override void ChangePresentation(MvxPresentationHint hint)
{
if (Activity.GetType() != typeof (MainActivity))
{
// if we are not on the Forms Activity, we assume, we are on an Android Activity
_androidPesenter.ChangePresentation(hint);
return;
}
base.ChangePresentation(hint);
}
public override void Show(MvxViewModelRequest request)
{
if (_androidViews.Contains(request.ViewModelType))
{
_androidPesenter.Show(request);
return;
}
base.Show(request);
}
}
Чтобы зарегистрировать наш новый ViewPresenter, мы должны переопределить CreateViewPresenter
в нашей настройке
public class Setup : MvxAndroidSetup
{
// ...
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var presenter = new HybridDroidViewPresenter();
Mvx.RegisterSingleton<IMvxViewPresenter>(presenter);
return presenter;
}
}
Наш ViewPresenter и MvxAndroidViewPresenter
потребности IMvxAndroidCurrentTopActivity
, Таким образом, мы должны дать ему знать о жизненном цикле нашего MainActivity
, Это можно сделать через IMvxAndroidActivityLifetimeListener
и изменив действие следующим образом:
[Activity(Label = "MainActivity", ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity
: FormsApplicationActivity
{
private IMvxAndroidActivityLifetimeListener _lifetimeListener;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Forms.Init(this, bundle);
var mvxFormsApp = new MvxFormsApp();
LoadApplication(mvxFormsApp);
var presenter = (MvxFormsDroidPagePresenter) Mvx.Resolve<IMvxViewPresenter>();
presenter.MvxFormsApp = mvxFormsApp;
Mvx.Resolve<IMvxAppStart>().Start();
_lifetimeListener = Mvx.Resolve<IMvxAndroidActivityLifetimeListener>();
_lifetimeListener.OnCreate(this);
}
protected override void OnDestroy()
{
_lifetimeListener.OnDestroy(this);
base.OnDestroy();
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
_lifetimeListener.OnViewNewIntent(this);
}
protected override void OnResume()
{
base.OnResume();
_lifetimeListener.OnResume(this);
}
protected override void OnPause()
{
_lifetimeListener.OnPause(this);
base.OnPause();
}
protected override void OnStart()
{
base.OnStart();
_lifetimeListener.OnStart(this);
}
protected override void OnRestart()
{
base.OnRestart();
_lifetimeListener.OnRestart(this);
}
protected override void OnStop()
{
_lifetimeListener.OnStop(this);
base.OnStop();
}
}
Теперь мы можем перейти к нашему FirstViewModel
с помощью
ShowViewModel<FirstViewModel>()
и FirstActivity выглядит как обычное действие MvvMCross Android и устанавливает FirstView.axml
как ContentView
,
[Activity(Label = "FirstActivity")]
public class FirstActivity : MvxActivity<FirstViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.FirstView);
}
}
Быть осведомленным:
- При этом вы не можете перейти к форме просмотра форм без просмотра форм (кроме просмотра форм), кроме как вернуться назад через
Close()
или кнопка возврата - Могут возникнуть некоторые проблемы с жизненным циклом (например, надгробие)
- Разработчики MvvMCross недовольны поддержкой форм в MvvMCross, поэтому в Forms Presenter могут произойти серьезные изменения.