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模拟器的配置问题。

  • 相关阅读:
    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(二)
    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)
    iOS开发UI篇—简单介绍静态单元格的使用
    iOS开发UI篇—UITableview控件使用小结
    ios开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
    iOS开发UI篇—使用xib自定义UItableviewcell实现一个简单的团购应用界面布局
    iOS开发UI篇—使用嵌套模型完成的一个简单汽车图标展示程序
    iOS开发UI篇—实现UItableview控件数据刷新
    iOS开发UI篇—推荐两个好用的Xcode插件(提供下载链接)
    人人都应学习的公链知识——比原总体架构
  • 原文地址:https://www.cnblogs.com/phoenixdong/p/6557500.html
Copyright © 2011-2022 走看看