对于一个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的生命周期的更多相关文章

  1. Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期

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

  2. Unity应用架构设计(9)——构建统一的 Repository

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

  3. View和viewController的生命周期

    View和viewController的生命周期 一.ViewController的职责 对内管理与之关联的View,对外跟其他ViewController通信和协调.对于与之关联的View,View ...

  4. vue第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期)

    第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期) #课程目标 掌握安装 vue-cli 命令行工具的方法,掌握使用命令行在本地搭建开发环境,使用命令行 ...

  5. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)

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

  6. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 1)

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

  7. Unity应用架构设计(2)——使用中介者模式解耦ViewModel之间通信

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

  8. iOS view和viewController的生命周期

    一.ViewController的职责 对内管理与之关联的View,对外跟其他ViewController通信和协调.对于与之关联的View,ViewController总是在需要的时候才加载视图,并 ...

  9. iOS viewController 和 view 的创建消失生命周期总结

    控制器创建的生命周期 1. 如果从stroryBoard 中产生一个controller,那么会先调用initWithCoder:, awakeFromNib, loadView,viewDidLoa ...

随机推荐

  1. Painting the Fence Gym - 101911E(构造)

    There is a beautiful fence near Monocarp's house. The fence consists of nn planks numbered from left ...

  2. hdu1285 确定比赛名次【拓扑排序】

    题目链接 确定比赛名次                                         Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  3. XamarinSQLite教程添加索引

    XamarinSQLite教程添加索引 索引可以提升数据库表的查询速度.下面为已存在的表添加索引,操作步骤如下: (1)右击Students,选择Add index…(beta)命令,弹出Add In ...

  4. Wannafly Winter Camp 2019.Day 8 div1 I.岸边露伴的人生经验(FWT)

    题目链接 \(Description\) 给定\(n\)个十维向量\(\overrightarrow{V_i}=x_1,x_2,...,x_{10}\).定义\(\overrightarrow{V}= ...

  5. ES6快速入门(三)类与模块

    类与模块 一.类 一)类的声明 class Person { constructor(name) { this.name = name; } sayName() { console.log(this. ...

  6. tomcat端口被占用的问题

    在dos下,输入  netstat   -ano|findstr  8080  //说明:查看占用8080端口的进程 显示占用端口的进程 taskkill  /pid  6856  /f  //说明, ...

  7. 潭州课堂25班:Ph201805201 爬虫高级 第十二 课 Scrapy-redis分布 项目实战 (课堂笔记)

    建代理池, 1,获取多个网站的免费代理IP, 2,对免费代理进行检测,>>>>>携带IP进行请求, 3,检测到的可用IP进行存储, 4,实现api接口,方便调用, 5,各 ...

  8. 基于Systick系统时钟延时的LED闪烁灯

    1.回顾我们的51 单片机编程,当我们需要做系统延迟的时候,最常采用的一种方式就是使用for 循环的空语句等待来实现. 当然,在STM32 里面也可以这么实现.但是在STM32 的Cortex 内核里 ...

  9. GDOI2017爆炸记

    100种方法教你爆零.. 总结 其实这一次比赛除了三个sb的错误还是收获到了很多的.. 起码自己已经知道自己有进队的实力 不足的地方很大 主要是脑子不太好使,题目要不只能拿最暴力的分要不就能a 看了很 ...

  10. GMA Round 1 年货

    传送门 年货 三角形的年货有没有见过啊?(如下图所示,图中共有12层小三角形,共计144个) 啊,不,这不是真正的年货,真正的年货是正六边形的!(这是什么设定?) 总之,麻烦你在图中找出顶点在三角形格 ...