1、什么是MVVM
借用一下百度百科上對MVVM的介紹,MVVM是Model-View-ViewModel的簡寫,它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行為抽象化,讓我們將視圖 UI 和業務邏輯分開。當然這些事 ViewModel 已經幫我們做了,它可以取出 Model 的數據同時幫忙處理 View 中由於需要展示內容而涉及的業務邏輯。
2、MVVM在unity開發中的應用
MVVM框架應用面十分廣泛,通常在前端開發中應用很廣,最近看到周圍同事在開發WPF時用到了這個框架,抱着好奇的態度來學習一下這個框架,MVVM框架在unity開發中同樣適用,在unity中,將每個UI抽象成一個個View,通常我們為每一個UI面板定義一個View,View中包含了該面板中涉及到的UI元素,比如一個Text,一個Button;每一個View都有獨立的ViewModel來管理,ViewModel中提供必要的屬性和方法來控制View, 而Model只是單純的定義一個數據模型。前兩天在github上發現了這個叫uMVVM的框架,拿來試用了一下,下面分析一下該框架對MVVM模式的設計。
3、uMVVM的設計與實現
下載uMVVM后,它提供了一些使用示例:
在這個示例中,有兩個panel,就定義了兩個view,每個view中定義該界面的元素,比如SetupView:
public class SetupView:UnityGuiView<SetupViewModel> { public InputField nameInputField; public Text nameMessageText; public InputField jobInputField; public Text jobMessageText; public InputField atkInputField; public Text atkMessageText; public Slider successRateSlider; public Text successRateMessageText; public Toggle joinToggle; public Button joinInButton; public Button waitButton; public SetupViewModel ViewModel { get { return (SetupViewModel)BindingContext; } } }
可以看到,View中需要指定對應的ViewModel來管理該View,ViewModel中定義的屬性需要具備當數據改變時通知訂閱者的功能,因此uMVVM對這種屬性進行了一層封裝,具體設計如下:
public class BindableProperty<T> { public delegate void ValueChangedHandler(T oldValue, T newValue); public ValueChangedHandler OnValueChanged; private T _value; public T Value { get { return _value; } set { if (!Equals(_value, value)) { T old = _value; _value = value; ValueChanged(old, _value); } } } private void ValueChanged(T oldValue, T newValue) { if (OnValueChanged != null) { OnValueChanged(oldValue, newValue); } } public override string ToString() { return (Value != null ? Value.ToString() : "null"); } }
可以看到,BindableProperty類中維護一個T類型數據,當T發生變化時,可以通知到訂閱者,有了這種屬性之后,那么ViewModel就是這樣的了:
public class SetupViewModel:ViewModelBase { public readonly BindableProperty<string> Name = new BindableProperty<string>(); public readonly BindableProperty<string> Job=new BindableProperty<string>(); public readonly BindableProperty<int> ATK = new BindableProperty<int>(); public readonly BindableProperty<float> SuccessRate=new BindableProperty<float>(); public readonly BindableProperty<State> State=new BindableProperty<State>(); }
uMVVM的設計中,每個View都繼承UnityGuiView這個泛型類,UnityGuiView大概的內容是這樣:
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>(); public T BindingContext { get { return ViewModelProperty.Value; } set { if (!_isInitialized) { OnInitialize(); _isInitialized = true; } //觸發OnValueChanged事件 ViewModelProperty.Value = value; } } /// <summary> /// 初始化View,當BindingContext改變時執行 /// </summary> protected virtual void OnInitialize() { //無所ViewModel的Value怎樣變化,只對OnValueChanged事件監聽(綁定)一次 ViewModelProperty.OnValueChanged += OnBindingContextChanged; } /// <summary> /// 當gameObject將被銷毀時,這個方法被調用 /// </summary> public virtual void OnDestroy() { if (BindingContext.IsRevealed) { Hide(true); } BindingContext.OnDestory(); BindingContext = null; ViewModelProperty.OnValueChanged = null; } /// <summary> /// 綁定的上下文發生改變時的響應方法 /// 利用反射+=/-=OnValuePropertyChanged /// </summary> public virtual void OnBindingContextChanged(T oldValue, T newValue) { Binder.Unbind(oldValue); Binder.Bind(newValue); } }
UnityGuiView中有一個BindingContext的屬性, 當使用框架時,需要給View的BindingContext指定對應的ViewModel:
public class Install:MonoBehaviour { // Use this for initialization public SetupView setupView; public TestView testView; void Start() { //綁定上下文 setupView.BindingContext=new SetupViewModel(); testView.BindingContext=new TestViewModel(); } }
在View中,就訂閱model數據改變的消息,並定義相應的響應函數:
public class SetupView:UnityGuiView<SetupViewModel> { //......省略ui元素的定義 protected override void OnInitialize() { base.OnInitialize(); Binder.Add<string>("Name", OnNamePropertyValueChanged); Binder.Add<string>("Job",OnJobPropertyValueChanged); Binder.Add<int>("ATK",OnATKPropertyValueChanged); Binder.Add<float>("SuccessRate",OnSuccessRatePropertyValueChanged); Binder.Add<State>("State",OnStatePropertyValueChanged); } private void OnSuccessRatePropertyValueChanged(float oldValue, float newValue) { successRateMessageText.text = newValue.ToString("F2"); } private void OnATKPropertyValueChanged(int oldValue, int newValue) { atkMessageText.text = newValue.ToString(); } private void OnJobPropertyValueChanged(string oldValue, string newValue) { jobMessageText.text = newValue.ToString(); } private void OnNamePropertyValueChanged(string oldValue, string newValue) { nameMessageText.text = newValue.ToString(); } private void OnStatePropertyValueChanged(State oldValue, State newValue) { //dosomething }
最后看一下其中一個model的定義:
public class Combatant { public int Id { get; set; } public string Name { get; set; } public string Job { get; set; } public float SuccessRate { get; set; } public State State { get; set; } } public enum State { JoinIn, Wait }
4、總結
本文只大概寫了一下uMVVM框架的一些設計和使用方法,不全面,如果感興趣,可以自行閱讀源碼,github地址為
https://github.com/MEyes/uMVVM
如有錯誤,歡迎指正!