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上,点击此了解

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java 成神之路

spring 之 import标签、alias标签、beans标签 解析

58410
来自专栏Java呓语

旧问新解·ListView 中的 OnItemSelectedListener 不生效

今天在写颜色识别的Demo 时有个场景是需要用户做出单项选择,脑中蹦出首选的方案就是 ListView 配合 ChoiceMode。

812
来自专栏Android 开发学习

data-binding 踩坑记

2104
来自专栏向治洪

Android杀毒实现原理及实例

一个杀毒软甲最核心的部分一个是病毒库一个是杀毒引擎,病毒库从服务器中获得,杀毒引擎实际上是判断程序中的包名和签名是否匹配病毒库中的包名和签名,如果匹配则为病毒,...

2807
来自专栏葡萄城控件技术团队

扩展GridView控件——为内容项添加拖放及分组功能

引言 相信大家对GridView都不陌生,是非常有用的控件,用于平铺有序的显示多个内容项。打开任何WinRT应用或者是微软合作商的网站,都会在APP中发现Gri...

3575
来自专栏cmazxiaoma的架构师之路

Android多线程+单线程+断点续传+进度条显示下载

5453
来自专栏Android开发指南

Android优化指南

5137
来自专栏Java成神之路

Eclipse插件开发_学习_02_GEF入门实例

(2)搜索 editors,选择 org.eclipse.ui.editors  扩展点,finish

5192
来自专栏杂烩

websocket 原

     WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。   

1912
来自专栏jianhuicode

学问Chat UI(3)

前言 上文学问Chat UI(2)分析了消息适配器的实现; 本文主要学习下插件功能如何实现的.并以图片插件功能作为例子详细说明,分析从具体代码入手; 概要 分析...

3086

扫码关注云+社区

领取腾讯云代金券