zoukankan      html  css  js  c++  java
  • 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上,点击此了解

  • 相关阅读:
    memcached与redis的一些区别小结
    ubuntu下定时任务
    hdu4135 容斥定理
    hdu1251 统计难题 字典树
    hdu1710 Binary Tree Traversals(二叉树的遍历)
    Codeforces Round #364 (Div. 2) B. Cells Not Under Attack
    KMP算法 hdu4686 Oulipo
    判断字符串是否在字典里
    floyd算法 poj2253
    最短路径dijkstra算法
  • 原文地址:https://www.cnblogs.com/OceanEyes/p/view_viewmodel_lifecycle.html
Copyright © 2011-2022 走看看