Unity应用架构设计(3)——构建View和ViewModel的生命周期
对于一个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图如下所示:

- 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<T>:MonoBehaviour,IView<T> where T:ViewModelBase
{
private bool _isInitialized;
public bool destroyOnHide;
protected readonly PropertyBinder<T> Binder=new PropertyBinder<T>();
public readonly BindableProperty<T> ViewModelProperty = new BindableProperty<T>();
/// <summary>
/// 显示之后的回掉函数
/// </summary>
public Action RevealedAction { get; set; }
/// <summary>
/// 隐藏之后的回掉函数
/// </summary>
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();
}
/// <summary>
/// 初始化View,当BindingContext改变时执行
/// </summary>
protected virtual void OnInitialize()
{
//无所ViewModel的Value怎样变化,只对OnValueChanged事件监听(绑定)一次
ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}
/// <summary>
/// 激活gameObject,Disable->Enable
/// </summary>
public virtual void OnAppear()
{
gameObject.SetActive(true);
BindingContext.OnStartReveal();
}
/// <summary>
/// 开始显示
/// </summary>
/// <param name="immediate"></param>
private void OnReveal(bool immediate)
{
if (immediate)
{
//立即显示
transform.localScale = Vector3.one;
GetComponent<CanvasGroup>().alpha = 1;
}
else
{
StartAnimatedReveal();
}
}
/// <summary>
/// alpha 0->1 之后执行
/// </summary>
public virtual void OnRevealed()
{
BindingContext.OnFinishReveal();
//回掉函数
if (RevealedAction!=null)
{
RevealedAction();
}
}
private void OnHide(bool immediate)
{
BindingContext.OnStartHide();
if (immediate)
{
//立即隐藏
transform.localScale = Vector3.zero;
GetComponent<CanvasGroup>().alpha = 0;
}
else
{
StartAnimatedHide();
}
}
/// <summary>
/// alpha 1->0时
/// </summary>
public virtual void OnHidden()
{
//回掉函数
if (HiddenAction!=null)
{
HiddenAction();
}
}
/// <summary>
/// 消失 Enable->Disable
/// </summary>
public virtual void OnDisappear()
{
gameObject.SetActive(false);
BindingContext.OnFinishHide();
if (destroyOnHide)
{
//销毁
Destroy(this.gameObject);
}
}
/// <summary>
/// 当gameObject将被销毁时,这个方法被调用
/// </summary>
public virtual void OnDestroy()
{
if (BindingContext.IsRevealed)
{
Hide(true);
}
BindingContext.OnDestory();
BindingContext = null;
ViewModelProperty.OnValueChanged = null;
}
/// <summary>
/// scale:1,alpha:1
/// </summary>
protected virtual void StartAnimatedReveal()
{
var canvasGroup = GetComponent<CanvasGroup>();
canvasGroup.interactable = false;
transform.localScale = Vector3.one;
canvasGroup.DOFade(1, 0.2f).SetDelay(0.2f).OnComplete(() =>
{
canvasGroup.interactable = true;
});
}
/// <summary>
/// alpha:0,scale:0
/// </summary>
protected virtual void StartAnimatedHide()
{
var canvasGroup = GetComponent<CanvasGroup>();
canvasGroup.interactable = false;
canvasGroup.DOFade(0, 0.2f).SetDelay(0.2f).OnComplete(() =>
{
transform.localScale = Vector3.zero;
canvasGroup.interactable = true;
});
}
/// <summary>
/// 绑定的上下文发生改变时的响应方法
/// 利用反射+=/-=OnValuePropertyChanged
/// </summary>
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上,点击此了解
Unity应用架构设计(3)——构建View和ViewModel的生命周期的更多相关文章
- Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期
> 对于一个View而言,本质上是一个MonoBehaviour.它本身就具备生命周期这个概念,比如,Awake,Start,Update,OnDestory等.这些是非常好的方法,可以让开发者 ...
- Unity应用架构设计(9)——构建统一的 Repository
谈到 『Repository』 仓储模式,第一映像就是封装了对数据的访问和持久化.Repository 模式的理念核心是定义了一个规范,即接口『Interface』,在这个规范里面定义了访问以及持久化 ...
- View和viewController的生命周期
View和viewController的生命周期 一.ViewController的职责 对内管理与之关联的View,对外跟其他ViewController通信和协调.对于与之关联的View,View ...
- vue第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期)
第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期) #课程目标 掌握安装 vue-cli 命令行工具的方法,掌握使用命令行在本地搭建开发环境,使用命令行 ...
- Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)
MVVM回顾 经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在. View只关心怎样渲染,而ViewModel只 ...
- Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 1)
初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...
- Unity应用架构设计(2)——使用中介者模式解耦ViewModel之间通信
当你开发一个客户端应用程序的时候,往往一个单页会包含很多子模块,在不同的平台下,这些子模块又被叫成子View(视图),或者子Component(组件).越是复杂的页面,被切割出来的子模块就越多,子模块 ...
- iOS view和viewController的生命周期
一.ViewController的职责 对内管理与之关联的View,对外跟其他ViewController通信和协调.对于与之关联的View,ViewController总是在需要的时候才加载视图,并 ...
- iOS viewController 和 view 的创建消失生命周期总结
控制器创建的生命周期 1. 如果从stroryBoard 中产生一个controller,那么会先调用initWithCoder:, awakeFromNib, loadView,viewDidLoa ...
随机推荐
- 2601 电路维修 (双端队列bfs\优先队列bfs(最短路))
描述 Ha'nyu是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女Rika,从而被收留在地球上.Rika的家里有一辆飞行车.有一天飞行车的电路板突然出现了故障,导致无法启动. 电路板 ...
- appium环境搭建及项目实战
手机端自动化环境搭建比其他自动化环境搭建较为复杂,安装工具有点多,也会有很多坑,安装工具一定注意版本号对应问题. 一.我的电脑环境:win7 64位,安卓测试机4.4.2版本,Python3.6,a ...
- spring cloud 详解
https://www.cnblogs.com/qdhxhz/p/9601170.html SpringCloud(8)---zuul权限校验.接口限流 https://blog.csdn.net/c ...
- SW加载标准库时出现failed to create Toolboxlibrary object怎么办?
友情提示:Windows+r打开 输入smd 注意操作前要关闭solidworks, 重新打开软件. 下面就可以找到很多标准件了.
- BZOJ1482 : [Balkan2017]Cats
若猫和狗中至少有一个出现了$0$次,那么答案显然是$0$,否则若狮子出现了$0$次,那么显然无解. 那么现在至少有一个动物保持原地不同,其它动物恰好移动一次. 如果全部猫都不动而全部狗都动,那么可以贪 ...
- BZOJ4855 : [Jsoi2016]轻重路径
首先用树状数组维护dfs序来快速支持一个点子树大小的询问. 每次删掉一个叶子时,从根开始往叶子走,显然只有$2size[x]\leq size[father]$的点的父亲才有可能换重儿子. 从根开始往 ...
- Java weak reference
一个对象被回收,必须满足两个条件: 没有任何引用指向它和GC在运行.把所有指向某个对象的引用置空来保证这个对象在下次GC运行时被回收. Object c = new Car(); c = null; ...
- Java 读取 txt 文件内容到容器 List
方法一: 一.桌面上准备 DataObject.txt 文件,内容为: 二.打开 Eclipse,编写代码如下: import java.io.BufferedReader; import java. ...
- jQuery 学习03——HTML:捕获、设置、添加元素、删除元素、CSS类、CSS()方法、尺寸
jQuery - 获取内容text().html() 以及 val()和属性attr() jQuery 中非常重要的部分,就是操作 DOM 的能力. DOM = Document Object Mod ...
- CSS魔法堂:Transition就这么好玩
前言 以前说起前端动画必须使用JS,而CSS3为我们带来transition和@keyframes,让我们可以以更简单(声明式代替命令式)和更高效的方式实现UI状态间的补间动画.本文为近期对Tran ...