zoukankan      html  css  js  c++  java
  • 【xamarin + MvvmCross 从零开始】五、MvvmCross 详解 (3)

    前言

    上一篇我们讲了MvvmCross的数据绑定,这次我们讲一下 ViewModel对象。

    ViewModel 对象详解

    ViewModel对象是Mvvm框架的核心对象,Mvvm模型中起到连接Model与View的作用。ViewModel可以理解为对Model的一个包装,通过对Model进行包装,隐藏与View无关的内容,以方便View进行数据呈现。

    在MvvmCross框架内,ViewModel必须实现IMvxViewModel接口,MvvmCross对IMvxViewModel的默认实现 MvxViewModel。

    ViewModel的生命周期

    构造

    MvvmCross中ViewModel的构造一般有以下方式:

    • 通过导航到指定的ViewModel,MvvmCross将自动创建指定的ViewModel。
        public abstract class MvxNavigatingObject : MvxNotifyPropertyChanged
        {
            protected MvxNavigatingObject();
    
            protected IMvxViewDispatcher ViewDispatcher { get; }
    
            protected bool ChangePresentation(MvxPresentationHint hint);
            protected bool Close(IMvxViewModel viewModel);
            protected bool ShowViewModel(Type viewModelType, object parameterValuesObject, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel(Type viewModelType, IDictionary<string, string> parameterValues, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel(Type viewModelType, IMvxBundle parameterBundle = null, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel<TViewModel>(object parameterValuesObject, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel>(IDictionary<string, string> parameterValues, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel>(IMvxBundle parameterBundle = null, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel, TInit>(TInit parameter, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModelInitializer<TInit>;
        }

    在ViewModel的父类 MvxNavigationObject中,对ShowViewModel有多个重载,分别可以通过指定ViewModel的类型以及导航参数进行导航。MvvmCross通过指定的ViewModel的类型查找相关联的视图,构建或查找已经存在的视图,并构建或查找缓存中的ViewModel,将ViewModel作为视图的DataContext注入到视图中,并根据对视图显示的配置,显示相应的视图。通常情况下,视图都将做为一个窗口进行显示。

    • 通过代码手动创建对象,直接调用ViewModel对象的构造函数进行创建。

    有些情况下,我们可能会单独创建视图进行显示,这时视图不会自动创建相关联的ViewModel,这时我们就需要手工进行创建,并将创建的ViewModel赋值给视图的DataContext属性或是ViewModel属性。

                var loginView = new LoginView();
                var loginViewModl = new LoginViewModel();
                loginView.ViewModel = loginViewModel;
    

    初始化

    在ViewModel的构造时我们提到在导航到指定的ViewModel时,可以传入导航参数,那么,导航参数如何在ViewModel中使用呢?这里就涉及到ViewModel的初始化方法 Init()。

    初始化方法有以下约束:

    • 名称必须是 Init并且无返回值;
    • 参数可以有多个参数,参数类型是简单类型,如字符串、整型、浮点型、Guid、枚举
    • 参数可以是一个对象,此对象的属性类型必须是简单类型
    • 还有另外一种初始化的方法,重载 InitFromBundle 方法。

    Init方法可以根据需要声明零到多个,MvvmCross将根据每次传入参数不同,匹配不同的初始化方法。

    Init方法会在ViewModel对象被构造以后或者在 ReloadState 和 Start 方法之后被调用。

    例如,我们的导航方法为:

    ShowViewModel<DetailViewModel>(new { First="Hello", Second="World", Answer=42 });

    那么我们可以这么获取导航参数:

    public class DetailViewModel : MvxViewModel
    {
      // ...
    
      public void Init(string First, string Second, int Answer)
      {
        // use the values
      }
    
      // ...
    }

    或者

    public class DetailViewModel : MvxViewModel
    {
      // ...
    
      public class NavObject
      {
        public string First {get;set;}
        public string Second {get;set;}
        public int Answer {get;set;}
      }
    
      public void Init(NavObject navObject)
      {
      // use navObject
      }
    
      // ...
    }

    或者

    public class DetailViewModel : MvxViewModel
    {
      // ...
    
      public override void InitFromBundle(IMvxBundle bundle)
      {
        // use bundle - e.g. bundle.Data["First"]
      }
    
      // ...
    }

    墓碑状态

    Crying face墓碑是什么?说简单点,就是手机上一个任务被迫中断时(如有电话打入),系统记录下当前应用程序的状态后,(像把事件记录在墓碑上一样),然后中止程序。当需要恢复时,根据“墓碑”上的内容,将程序恢复到中断之前的状态。这样的一种机制就是“墓碑机制”。只是叫法不一样,实际在Android和iOS中都有类似的状态。在Android中叫Stop,在iOS中viewWillDisappear。

    既然有墓碑状态,那么就可以从墓碑状态中恢复,ReloadFromBundle方法就是从墓碑状态中恢复保存的数据的方法。

    启动

    当ViewModel 从按顺序执行完构建、Init、ReloadState 恢复后,就会调用Start 方法。启动方法是一个无参的方法。

    public class DetailViewModel : MvxViewModel
    {
      // ...
    
      public override void Start()
      {
        // do any start
      }
    
      // ...
    }

    ViewModel导航

    启动导航

    启动导航,是指在系统启动时的第一个窗口。指定启动导航窗口,一般是在App.cs中指定:

    Mvx.RegisterAppStart<TipViewModel>();

    这样,当系统启动时,MvvmCross会查找指定ViewModel关联的View,并将查找到的View作用第一个窗口进行显示。

    在Android系统中,可以指定 SplashScreenActivity 以实现启动页的功能。

    • 删除目前启动首页的设置,在目前主窗口的Activity的Activity特性标签上,移除 MainLauncher=true,表示不再将主窗口作为App启动的第一个窗口。
    • 添加新的启动页的布局:
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">
       <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:gravity="center"
          android:text="loading..." />
    </FrameLayout>
    • 添加新的启动页的Activity。这里我们将 MainLauncher 设置为true,表示此窗口为App启动的第一个窗口。代码如下:
    using Android.App;
    using Cirrious.MvvmCross.Droid.Views;
    
    namespace CalcApp.UI.Droid
    {
        [Activity(Label = "My App", MainLauncher = true, NoHistory = true, Icon = "@drawable/icon")]
        public class SplashScreenActivity : MvxSplashScreenActivity
        {
            public SplashScreenActivity() : base(Resource.Layout.SplashScreen)
            {
            }
        }
    }

    有时我们需要根据不同的场景,启动不同的启动页,这时通过指定单一的启动页就不能满足我们的需求。MvvmCross也支持自定义启动页。

    • 实现自定义的启动对象:
    public class CustomAppStart : MvxNavigatingObject, IMvxAppStart
    {
        private readonly ILoginService _service;
    
        public CustomAppStart(ILoginService service)
        {
            _service = service;
        }
    
        public void Start(object hint = null)
        {
            if (!_service.IsLoggedIn)
            {
                ShowViewModel<LoginViewModel>();
            }
            else
            {
                ShowViewModel<TipViewModel>();
            }
        }
    }

    启动对象在启动时将根据用户的登录状态不同,显示不同的启动窗口。

    • 使用自定义的启动对象进行启动,在App中,指定启动对象:
    RegisterAppStart(CustomAppStart);
    

    跳转导航

    在ViewModel之间进行导航,可以通过ShowViewModel 方法进行导航。我们来看一下ShowViewModel的定义:

    namespace MvvmCross.Core.ViewModels
    {
        public abstract class MvxNavigatingObject : MvxNotifyPropertyChanged
        {
            protected MvxNavigatingObject();
    
            protected IMvxViewDispatcher ViewDispatcher { get; }
    
            protected bool ChangePresentation(MvxPresentationHint hint);
            protected bool Close(IMvxViewModel viewModel);
            protected bool ShowViewModel(Type viewModelType, object parameterValuesObject, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel(Type viewModelType, IDictionary<string, string> parameterValues, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel(Type viewModelType, IMvxBundle parameterBundle = null, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null);
            protected bool ShowViewModel<TViewModel>(object parameterValuesObject, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel>(IDictionary<string, string> parameterValues, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel>(IMvxBundle parameterBundle = null, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModel;
            protected bool ShowViewModel<TViewModel, TInit>(TInit parameter, IMvxBundle presentationBundle = null, MvxRequestedBy requestedBy = null) where TViewModel : IMvxViewModelInitializer<TInit>;
        }
    }
    • ShowViewModel有多个重载,可以根据需要,调用不同的重载。
    • 在调用时可以传入导航参数,以达到在模块间传递参数的需要。
    • 导航参数必须是简单类型的参数,包括整型、浮点型、字符串、布尔、枚举等类型。
    • 在被导航的ViewModel中,需要定义相应的  Init  方法来接收导航参数。
    • 在需要关闭指定的窗口时,可通过ViewModel中的  Close  方法进行关闭指定ViewModel相联的窗口。

    小结

    本篇主要讲解了ViewModel对象的生命周期以及如何通过ViewModel进行导航。如有问题请留言,我会尽快回复。

    下节我们说下Android和iOS模拟器的配置问题。

  • 相关阅读:
    [Luogu P3626] [APIO2009] 会议中心
    杭电 1869 六度分离 (求每两个节点间的距离)
    杭电 1874 畅通工程续 (求某节点到某节点的最短路径)
    最短路径模板
    杭电 2544 最短路径
    POJ 1287 Networking (最小生成树模板题)
    NYOJ 1875 畅通工程再续 (无节点间距离求最小生成树)
    POJ 2485 Highways (求最小生成树中最大的边)
    杭电 1233 还是畅通工程 (最小生成树)
    杭电 1863 畅通工程 (最小生成树)
  • 原文地址:https://www.cnblogs.com/phoenixdong/p/6557500.html
Copyright © 2011-2022 走看看