> 对于一个View而言,本质上是一个MonoBehaviour。它本身就具备生命周期这个概念,比如,Awake,Start,Update,OnDestory等。这些是非常好的方法,可以让开发者在各个阶段去执行自定义的代码。但唯一遗憾的事,这些方法是有引擎调用,并且颗粒度不够细。本文将谈谈怎样构建View和ViewModel的生命周期。

### View的生命周期 ###
**举个栗子,一个View的显示会有如下过程:**

- 初始化操作
- 激活当前对象,SetActive(true)
- 显示当前对象,包括localScale=Vector3.one,并且alpha从0->1
- 当View显示之后,执行某些callBack方法,OnCompleted或者OnSuccess

**再举个栗子,一个View隐藏会有如下过程:**

- 隐藏当前对象,包括localScale=Vector3.zero,并且alpha从1->0
- 当View隐藏之后,执行某些callBack方法,OnCompleted或者OnSuccess
- 不激活当前对象,SetActive(false)
- Destory 当前对象时的处理方法

### ViewModel的生命周期 ###

对于View而言,它并不处理复杂的业务逻辑,View只负责显示。比如在哪个阶段去数据库或者其他地方去拿数据,这不归View来处理。这理所应当交给ViewModel去处理,ViewModel只要知道View什么阶段让我去拿数据即可。

所以对应的ViewModel也有生命周期,它对应了View的生命周期,ViewModel的生命周期包括:

- 初始化操作
- View在**显示前**处理的逻辑
- View在**显示后**时处理的逻辑
- View在**隐藏前**处理的逻辑
- View在**隐藏后**处理的逻辑
- View被**销毁时**应该处理的逻辑

### 构建生命周期 ###

有了上述的分析之后,就需要落实,如何去构建View和ViewModel的生命周期了。

Overview图如下所示:

![](http://images.cnblogs.com/cnblogs_com/OceanEyes/836627/o_lift.jpg)

- OnInitialize:用来初始化View。结合前几篇文章,OnInitialize 用来注册 **OnBindingContextChanged** 事件以及属性绑定(Binder.Add)
- OnAppear:用来激活View
- OnReveal:用来显示View,比如以动画形式(Fade)显示呢还是直接显示
- OnRevealed:当View显示完毕时,执行的额外操作,是一个委托(**Action**)
- OnHide:开始隐藏View
- OnHidden:同OnReveal一样,可以以动画形式慢慢隐藏或者直接隐藏
- OnDisappear:隐藏完毕后SetActive(false)不激活当前对象
- OnDestory:当View被Detory时自动调用OnDestory方法

将这些方法放入UnityGuiView基类中:

[RequireComponent(typeof(CanvasGroup))]
public abstract class UnityGuiView:MonoBehaviour,IView where T:ViewModelBase
{
private bool _isInitialized;
public bool destroyOnHide;
protected readonly PropertyBinder Binder=new PropertyBinder();
public readonly BindableProperty ViewModelProperty = new BindableProperty();
///

/// 显示之后的回掉函数
///

public Action RevealedAction { get; set; }
///

/// 隐藏之后的回掉函数
///

public Action HiddenAction { get; set; }

public T BindingContext
{
get { return ViewModelProperty.Value; }
set
{
if (!_isInitialized)
{
OnInitialize();
_isInitialized = true;
}
//触发OnValueChanged事件
ViewModelProperty.Value = value;
}
}

public void Reveal(bool immediate = false, Action action = null)
{
if (action!=null)
{
RevealedAction += action;
}
OnAppear();
OnReveal(immediate);
OnRevealed();
}

public void Hide(bool immediate = false, Action action = null)
{
if (action!=null)
{
HiddenAction += action;
}
OnHide(immediate);
OnHidden();
OnDisappear();
}

///

/// 初始化View,当BindingContext改变时执行
///

protected virtual void OnInitialize()
{
//无所ViewModel的Value怎样变化,只对OnValueChanged事件监听(绑定)一次
ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}

///

/// 激活gameObject,Disable->Enable
///

public virtual void OnAppear()
{
gameObject.SetActive(true);
BindingContext.OnStartReveal();
}
///

/// 开始显示
///

///
private void OnReveal(bool immediate)
{
if (immediate)
{
//立即显示
transform.localScale = Vector3.one;
GetComponent().alpha = 1;
}
else
{
StartAnimatedReveal();
}
}
///

/// alpha 0->1 之后执行
///

public virtual void OnRevealed()
{
BindingContext.OnFinishReveal();
//回掉函数
if (RevealedAction!=null)
{
RevealedAction();
}
}

private void OnHide(bool immediate)
{
BindingContext.OnStartHide();
if (immediate)
{
//立即隐藏
transform.localScale = Vector3.zero;
GetComponent().alpha = 0;
}
else
{
StartAnimatedHide();
}
}
///

/// alpha 1->0时
///

public virtual void OnHidden()
{
//回掉函数
if (HiddenAction!=null)
{
HiddenAction();
}
}
///

/// 消失 Enable->Disable
///

public virtual void OnDisappear()
{
gameObject.SetActive(false);
BindingContext.OnFinishHide();
if (destroyOnHide)
{
//销毁
Destroy(this.gameObject);
}

}
///

/// 当gameObject将被销毁时,这个方法被调用
///

public virtual void OnDestroy()
{
if (BindingContext.IsRevealed)
{
Hide(true);
}
BindingContext.OnDestory();
BindingContext = null;
ViewModelProperty.OnValueChanged = null;
}

///

/// scale:1,alpha:1
///

protected virtual void StartAnimatedReveal()
{
var canvasGroup = GetComponent();
canvasGroup.interactable = false;
transform.localScale = Vector3.one;

canvasGroup.DOFade(1, 0.2f).SetDelay(0.2f).OnComplete(() =>
{
canvasGroup.interactable = true;
});
}
///

/// alpha:0,scale:0
///

protected virtual void StartAnimatedHide()
{
var canvasGroup = GetComponent();
canvasGroup.interactable = false;
canvasGroup.DOFade(0, 0.2f).SetDelay(0.2f).OnComplete(() =>
{
transform.localScale = Vector3.zero;
canvasGroup.interactable = true;
});
}
///

/// 绑定的上下文发生改变时的响应方法
/// 利用反射+=/-=OnValuePropertyChanged
///

private void OnBindingContextChanged(T oldValue, T newValue)
{
Binder.Unbind(oldValue);
Binder.Bind(newValue);
}
}

而ViewMode中就现对而言比较简单了,处理View处理不了的逻辑:

public virtual void OnStartReveal()
{
IsRevealInProgress = true;
//在开始显示的时候进行初始化操作
if (!_isInitialized)
{
OnInitialize();
_isInitialized = true;
}
}

public virtual void OnFinishReveal()
{
IsRevealInProgress = false;
IsRevealed = true;
}

public virtual void OnStartHide()
{
IsHideInProgress = true;

}

public virtual void OnFinishHide()
{
IsHideInProgress = false;
IsRevealed = false;
}

public virtual void OnDestory()
{

}

> 值得注意的事,以上不管是View还是ViewModel与生命周期相关的方法,都是虚方法(**virtual**),这就意味这子类可以Override掉。比如某些场景下需要将View从左边或者右边移入,可以在初始化时指定偏移距离。又或者不想用默认的DoTween特效,你也可以完全Override并使用Animation等。

### 小结 ###
本文介绍了怎样为View/ViewModel构建自定义的生命周期,MonoBehaviour 虽然有自己的生命周期,但不够细腻,我们完全可以扩展自己的生命周期,实现对需求的定制。
源代码托管在Github上,[点击此了解](https://github.com/MEyes/uMVVM)
你也可以从以下链接获取更多有关Unity 3D Framework的设计:
[Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 1)](http://www.cnblogs.com/OceanEyes/p/unity3d_framework_designing_get_started_with_mvvm_part1.html)
[Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 2)](http://www.cnblogs.com/OceanEyes/p/unity3d_framework_designing_get_started_with_mvvm_part2.html)
[Unity 3D Framework Designing(2)——使用中介者模式解耦ViewModel之间通信](http://www.cnblogs.com/OceanEyes/p/using_message_aggregator_mediator_communication_between_viewmodels.html)
[Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期](http://www.cnblogs.com/OceanEyes/p/view_viewmodel_lifecycle.html)

Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期的更多相关文章

  1. Unity应用架构设计(3)——构建View和ViewModel的生命周期

    对于一个View而言,本质上是一个MonoBehaviour.它本身就具备生命周期这个概念,比如,Awake,Start,Update,OnDestory等.这些是非常好的方法,可以让开发者在各个阶段 ...

  2. Unity 3D Framework Designing(9)——构建统一的 Repository

    谈到 『Repository』 仓储模式,第一映像就是封装了对数据的访问和持久化.Repository 模式的理念核心是定义了一个规范,即接口『Interface』,在这个规范里面定义了访问以及持久化 ...

  3. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 2)

    MVVM回顾 经过上一篇文章的介绍,相信你对 MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在.View只关心怎样渲染,而ViewModel只 ...

  4. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 1)

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  5. Unity 3D Framework Designing(4)——设计可复用的SubView和SubViewModel(Part 1)

    『可复用』这个词相信大家都熟悉,通过『可复用』的组件,可以大大提高软件开发效率. 值得注意的事,当我们设计一个可复用的面向对象组件时,需要保证其独立性,也就是我们熟知的『高内聚,低耦合』原则. 组件化 ...

  6. Unity 3D Framework Designing(6)——设计动态数据集合ObservableList

    什么是 『动态数据集合』 ?简而言之,就是当集合添加.删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面.有经验的程序员脑海里迸出的第一个词就是 ObservableCollection.没 ...

  7. Unity 3D Framework Designing(7)——IoC工厂理念先行

    一谈到 『IoC』,有经验的程序员马上会联想到控制反转,将创建对象的责任反转给工厂.IoC是依赖注入 『DI』 的核心,大名鼎鼎的Spring框架就是一个非常卓越的的控制反转.依赖注入框架.遗憾的是, ...

  8. Unity 3D Framework Designing(2)——使用中介者模式解耦ViewModel之间通信

    当你开发一个客户端应用程序的时候,往往一个单页会包含很多子模块,在不同的平台下,这些子模块又被叫成子View(视图),或者子Component(组件).越是复杂的页面,被切割出来的子模块就越多,子模块 ...

  9. Unity 3D Framework Designing(5)——ViewModel之间如何共享数据

    对于客户端应用程序而言,单页应用程序(Single Page Application)是最常见的表现形式.有经验的开发人员往往会把一个View分解多个SubView.那么,如何在多个SubView之间 ...

随机推荐

  1. MVC View显示详解(RenderBody,RenderPage,RenderSection,Partial)

    一.Views文件夹 -> Shared文件夹下的 _Layout.cshtml 母版页 @RenderBody 当创建基于_Layout.cshtml布局页面的视图时,视图的内容会和布局页面合 ...

  2. 张高兴的 UWP 开发笔记:用 Thumb 控件仿制一个可拖动 Button

    在 WPF 上可用的控件拖动方法在 UWP 上大多没用,那干脆用 Thumb 仿制一个吧. 关于 Thumb 控件的教程也不多,毕竟在 WPF 控件拖动有很多种方法, Thumb 就显得很鸡肋了.下面 ...

  3. C# Redis学习系列一:Redis的认识、下载、安装、使用

    一.认识Redis 1. Redis 是一个高性能的key-value数据库. 2. 它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sor ...

  4. [Linux] - xxx 不在 sudoers 文件中。此事将被报告。

    redhat系统: linux中创建用户命令为:useradd 用户名, eg: useradd test 指定密码:passwd test 但是有时候我们需要使用test运行执行一些root用户才有 ...

  5. 调用android方法,出现版本太低解决方法

    原因如下图所示: 调用需要API级别11,当前是8. 解决方法如下图所示: 点击

  6. CSS中padding和margin以及用法

    CSS中padding与margin 1.padding:内边距,表示控件内容相对于边缘的距离. 2.margin:外边距,表示控件边缘相对于父空间的边缘. 参考:http://www.studyof ...

  7. input 即时搜索 监听输入值的变化

    在 Web 开发中经常会碰到需要动态监听输入框值变化的情况,如果使用 onkeydown.onkeypress.onkeyup 这个几个键盘事件来监测的话,监听不了右键的复制.剪贴和粘贴这些操作,处理 ...

  8. Jenkins+Git 持续集成

    持续集成是必要的! 希望达到的效果是:开发同事签入代码后,对应的站点可以自动实现更新.目前还只在在内部服务器上实现开发环境中的站点更新,不涉及到线上的发布. 目前使用Jenkins实现这样的持续集成. ...

  9. npm学习总结

    1.npm run [scripts name]的作用及意义: npm 局部安装的工具包不能像全局安装那样直接执行命令行,但可写成命令行执行语句,通过npm run来运行,该命令可将node_modu ...

  10. 1609: [Usaco2008 Feb]Eating Together麻烦的聚餐

    1609: [Usaco2008 Feb]Eating Together麻烦的聚餐 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1010  Solv ...