MvvmCross:混合 Xamarin.Forms 和 Android Activity 应用
MvvmCross: Hybrid Xamarin.Forms and Android Activity Application
作为 MvvmCross 的新手,我决定创建一个小型 Xamarin.Forms 应用程序。我有一个 MainPage.xaml
绑定到它的 ViewModel MainViewModel.cs
,它首先显示。我在机器人项目中有一个 FirstView.axml
及其 activity。关联的 ViewModel 与 MainViewModel 一起位于核心项目中,并命名为 FirstViewModel.cs
单击导航按钮后,我希望 MvvmCross 显示 FirstView.axml
布局并绑定到 VM。然而,
每当调用命令时,我都会得到
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
[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: How to navigate from regular view to Mvvm viewmodel on Android?
在 MvvmCross 中,ViewPresenter 是处理视图之间导航的组件。
所以我们必须创建自己的
- 继承自
MvxFormsDroidPagePresenter
,因为我们有一个 Forms App
重定向
的非表单调用
ChangePresentation
(常用于Close())
Show
到处理活动的 ViewPresenter (MvxAndroidViewPresenter
)
- 非窗体视图模型列表(
_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 来完成:
[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 Activity 并将 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-Support 不满意,因此 Forms Presenter 可能会有一些重大变化
我最终做的是定义一个空接口 IXFViewModel
,我在 Forms
ViewModels 上实现了它。然后在我的自定义演示器中,我得到了 MvxViewModelRequest.ViewModelType
实现的接口列表。
如果在接口列表中找到类型 IXFViewModel
,则扩展 MvxFormsDroidPagePresenter
的自定义演示器会通过调用 base.Show(request)
来处理演示。如果未找到 IXFViewModel
类型,自定义演示器会切换到 MvxAndroidViewPresenter
来处理视图演示。
以下解决方案:
Bootstrap
public class Setup : MvxAndroidSetup
{
/// prefix ///
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var presenter = new XFDroidPresenter();
Mvx.RegisterSingleton<IMvxViewPresenter>(presenter);
return 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);
}
}
启动器和表单 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 的新手,我决定创建一个小型 Xamarin.Forms 应用程序。我有一个 MainPage.xaml
绑定到它的 ViewModel MainViewModel.cs
,它首先显示。我在机器人项目中有一个 FirstView.axml
及其 activity。关联的 ViewModel 与 MainViewModel 一起位于核心项目中,并命名为 FirstViewModel.cs
单击导航按钮后,我希望 MvvmCross 显示 FirstView.axml
布局并绑定到 VM。然而,
每当调用命令时,我都会得到
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
[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: How to navigate from regular view to Mvvm viewmodel on Android?
在 MvvmCross 中,ViewPresenter 是处理视图之间导航的组件。
所以我们必须创建自己的
- 继承自
MvxFormsDroidPagePresenter
,因为我们有一个 Forms App 重定向
的非表单调用ChangePresentation
(常用于Close())Show
到处理活动的 ViewPresenter (
MvxAndroidViewPresenter
)- 非窗体视图模型列表(
_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 来完成:
[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 Activity 并将 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-Support 不满意,因此 Forms Presenter 可能会有一些重大变化
我最终做的是定义一个空接口 IXFViewModel
,我在 Forms
ViewModels 上实现了它。然后在我的自定义演示器中,我得到了 MvxViewModelRequest.ViewModelType
实现的接口列表。
如果在接口列表中找到类型 IXFViewModel
,则扩展 MvxFormsDroidPagePresenter
的自定义演示器会通过调用 base.Show(request)
来处理演示。如果未找到 IXFViewModel
类型,自定义演示器会切换到 MvxAndroidViewPresenter
来处理视图演示。
以下解决方案:
Bootstrap
public class Setup : MvxAndroidSetup
{
/// prefix ///
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var presenter = new XFDroidPresenter();
Mvx.RegisterSingleton<IMvxViewPresenter>(presenter);
return 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);
}
}
启动器和表单 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); }
}
}