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上,點擊此了解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM