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图如下所示:

Unity应用架构设计(3)——构建View和ViewModel的生命周期_第1张图片

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

你可能感兴趣的:(Unity应用架构设计(3)——构建View和ViewModel的生命周期)