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 ...
随机推荐
- shell下获取系统时间
shell下获取系统时间的方法直接调用系统变量 获取今天时期:`date +%Y%m%d` 或 `date +%F` 或 $(date +%y%m%d) 获取昨天时期:`date -d yesterd ...
- 数据仓库分层ODS DW DM 主题 标签
数据仓库知识之ODS/DW/DM - xingchaojun的专栏 - CSDN博客 数据仓库为什么要分层 - 晨柳溪 - 博客园 数据仓库的架构与设计 - Trigl的博客 - CSDN博客 数据仓 ...
- tomcat端口被占用的问题
在dos下,输入 netstat -ano|findstr 8080 //说明:查看占用8080端口的进程 显示占用端口的进程 taskkill /pid 6856 /f //说明, ...
- rest framework 源码流程
1. def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the ...
- python网络编程(一)
socket简介 1.本地的进程间通信(IPC)有很多种方式,例如 队列 同步(互斥锁.条件变量等) 以上通信方式都是在一台机器上不同进程之间的通信方式,那么问题来了 网络中进程之间如何通信? 2. ...
- 创建xml文件、解析xml文件
1.创建XML文件: import codecs import xml.dom.minidom doc=xml.dom.minidom.Document() print doc root=do ...
- day8网络编程,面向对象1
一.只是回顾 1.导入模块的顺序,首先从当前目录下找,再从环境变量里面找,使用"sys.path.insert(0,'需要导入的环境变量')"加入需要导入文件的环境变量; 2.如果 ...
- 弱智的grub消除法
GRUB GNU GRUB(简称"GRUB")是一个来自GNU项目的启动引导程序.GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望 ...
- Python中将array类型不按科学计数法存在文件中的方法
直接上代码: from numpy import *import numpy as npDrug_array = zeros((708,708),dtype = int)f = open('D:\ma ...
- 小甲鱼Python第四讲课后习题
1while语句中,当条件为真时,它会一直循环下去,比如下面的例子,不过可以用Ctral + C来强制结束 while 'C': print("i love you") 2.观察打 ...