> 对于一个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. 使用JDBC连接数据库(一)

    JDBC是由java编程语言编写的类及接口组成,同时它为程序开发人员提供了一组用于实现对数据库访问的JDBC API,并支持SQL语言.利用JDBC可以将JAVA代码连接到oracle.DB2.SQL ...

  2. 通过 Chrome 在 Windows 中调试运行在 iphone-safari 上的 页面

    本文重点讨论如何在 Windows 系统中通过chrome 浏览器调试运行在 iPhone Safari 浏览器中的网页.如果你有一台 iMac/MacBook,可忽略该文档.iMac 环境下,直接通 ...

  3. 2017年2月16日 分析下为什么spring 整合mybatis后为啥用不上session缓存

    因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存. 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava 所以提出来纠结下 实验 ...

  4. JavaScript null 和 undefined

    null null 表示一个变量被声明了,并被赋值为空 var lzh = null; console.log(lzh); // null console.log(typeof lzh); // ob ...

  5. Azure机器学习入门(三)创建Azure机器学习实验

    在此动手实践中,我们将在Azure机器学习Studio中一步步地开发预测分析模型,首先我们从UCI机器学习库的链接下载普查收入数据集的样本并开始动手实践: http://archive.ics.uci ...

  6. 从并发处理谈PHP进程间通信(二)System V IPC

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  7. Lua与.net的CLR相互调用

    工程环境搭建: 下载luainterface-1.5.3.zip文件,使用到的dll为Built目录下的LuaInterface.dll.lua51.dll.luanet.dll LuaInterfa ...

  8. phpcms代码读取文章的内容 实用可行的方法

    在使用phpcms做网站的时候经常遇到读取网站的内容作为推荐,而不是描述.这里使用可行的方法交你如何读取内容推荐.方法有两个,第一种执行的效率低,第二个效率高些. 1. {pc:get sql=&qu ...

  9. MINA、Netty、Twisted一起学(十一):SSL/TLS

    什么是SSL/TLS 不使用SSL/TLS的网络通信,一般都是明文传输,网络传输内容在传输过程中很容易被窃听甚至篡改,非常不安全.SSL/TLS协议就是为了解决这些安全问题而设计的.SSL/TLS协议 ...

  10. Linux 命令--查看物理CPU个数、核数、逻辑CPU个数

    # 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...