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的生命周期

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

作者: 木宛城主

--电子创新网--
粤ICP备12070055号