關於Xamarin、Qml、數據綁定、MVC、MVVM 相關的散講

關於Xamarin、Qml、數據綁定、MVC、MVVM 相關的散講
SURFSKY 2017.02


最近又在學習Xamarin了?爲何是「又」?有幾個利好消息,讓我從新拾起它:
    (1)微軟去年收購了Xamarin,並且免費。原先的費用會嚇死人,並且按人頭+平臺來收費。
    (2)Xamarin.Forms 的出現,UI層也能夠複用了,不像原先只能邏輯層代碼複用。
    (3)IDE加強:
        Xamarin Forms Previewer,能夠直接預覽 XAML 的外觀。
        XAML 智能感應的加強,如今編寫XAML舒服多了。原先編寫XAML可不是件輕鬆的事情,因此以前的教材所有都是用C#寫的UI,這很不利於視圖和邏輯的分離。
        Visual Studio for mac 發佈,可替代原先的 Xamarin Studio for mac。畢竟,在mac下編寫ios/Android app不管性能仍是便利性都要比在windows上好得多。
        不愧是宇宙最強IDE開發商,一接手Xamarin,編輯器就加強了:)
    (4)真正的一個語言能夠編寫跨平臺移動app,包括後臺、ios、android、windows uwp 等
        數據接口和後臺:用 asp.net 寫便可
        移動客戶端API:Xamarin 已用C#封裝好,若是須要平臺專用功能,直接調用便可。
        這很利於節省成本、積累技術、簡化員工招招聘和培訓。
    (5)性能
        對於ios平臺,xamarin是將c#編譯成原生的代碼。android是嵌入了.NET運行時。
        實測試了下,ios下沒有任何問題,3秒左右進入主界面,進去後性能就和原生的沒有區別了
        android的啓動略慢,估計要5秒,進去後性能沒有問題。
        加個歡迎屏能夠稍微減緩客戶等待的焦慮感。

固然還有缺陷和指望
    缺陷
        Xamarin.Forms 的控件是轉化爲各平臺原生控件的,也就是說,這些控件在各平臺上外觀都有區別!
        IED
            複雜的 XAML previewer展現不出來。
            沒法拖拽控件快速設計頁面。
            VS MAC 有時候屏幕刷新不過來,一片白。
            常常出現「不是在活動配置中生成的項目」,沒法加載項目。
        啓動速度仍是慢了點,位於可容忍的邊緣。
        沒有官方的、統一的移動平臺功能 API 庫。
            相似PhoneGap那樣的,訪問攝像頭、傳感器等API,一套代碼各平臺通用。
            現有的這些功能API是分散在各平臺本身的庫裏的,學習一個統一庫與學習多平臺類庫,工做量固然不同。
            早些年有Xamarin.Mobile項目,但與Xamarin.Forms不兼容已經廢棄了。
            如今這些功能API插件處於散亂瑣碎狀態,要本身一個個去Xamarin Compoents/GitHub上找。
    指望
        不知何時能夠直接拖拽 Xamarin Forms 控件,一個平臺也行啊。
        加快 Xamarin Form Android app 啓動速度。
        須要一套外觀一致的 Xamarin Forms UI 庫,但短時間內不會有,要咱們直接寫了(*)
        官方推出統一的跨平臺移動功能 API 庫:Xamarin.Forms.Mobile(*)
    備註
        固然 Visual Studio for mac 仍是預覽版本,等正式版估計BUG會少一些。
        (*)打算啓動這兩個項目,歡迎有共同志向的同窗參與。

相比較 Qt
    性能仍是Qt佔優的,畢竟底層是C++寫的。但Xamarin的性能也在可容忍範圍內,主要是啓動時間稍慢了點。
    APP 大小。
        都在20M以上,
        Qt
            V-Play apps 基於Qt的遊戲引擎 52.4M
            新發布的 Qt 5.8聽說能夠裁剪庫的大小,將APP壓縮到四、5M內
        Xamarin 我也搞不清楚了。
            Xuni Explorer 只有 5.6 M
            Evlove 16 有46.1M
            隨便編譯個debug版本的,估計有60多M,release的還沒試驗
    雖說理論上 Qml 能夠擺平一切界面問題,但咱們不可避免的要涉及到各平臺的插件,那麼你真正須要學習的語言有:
        QML           : 
        Javascript    : 
        C++           : Qt類庫
        Object-C      : 
        Android       : 
    ps.真但願Qml能發展成獨立的開發語言(參照 swift)
        強類型
        可編譯
        簡化建立組件語法,直接用new MyItem(),而不是複雜的Qt.CreateComponent.....
        摒棄js、v4引擎,提供工具將js遷移爲標準Qml
        獨立發展 Qml 類庫
        更便利的方式訪問c++類庫,相似swift那樣,只需Import便可使用


綁定的前世此生
    若要監控一個對象的若干屬性變化狀況,傳統寫法是爲每一個要監控的屬性加上一個值變動事件,而後讓調用者添加這個事件
        public event Event FirstNameChanged;
        public string FirstName {get; set;}
        sample.FirstNameChanged += (o, value)=>txtbox.Text = value;
        這樣極可能要寫不少的屬性變動事件,並且不能自動給訂購者賦值
    什麼是綁定?它事實上作了兩件事
        (1)監聽對象指定屬性,若變化,則通知給訂購者
        (2)給訂購者賦值
    微軟給出的官方方案是
        (1)綁定系統維護了一個表格(在綁定表達式中指定),字段大體包括:
            object SourceObject         : 監聽對象
            string SourcePropertyName   : 監聽對象的屬性名稱
            object TargetObject         : 訂購者
            string ValuePath            : 賦值路徑
        (2)讓數據實現INotifyPropertyChanged接口
            public event PropertyChangedEventHandler PropertyChanged;
            若監聽對象的屬性有變更,則觸發這個事件,通知綁定系統
            這個事件的參數中包含了監聽對象和變動的屬性名稱(SourceObject + SourcePropertyName)
        (3)綁定系統去查表,給訂購者進行賦值
            TargetObject = SourceObject.SourcePropertyName.ValuePath

屬性變動通知
    INotifyPropertyChanged接口(屬性變動的時候通知下綁定系統,個人哪一個屬性變動了,快處理啊)
        class Sample : INotifyPropertyChanged
        {
            // INotifyPropertyChanged 接口成員
            public event PropertyChangedEventHandler PropertyChanged;

            // 屬性
            private string firstName;
            public string FirstName
            {
                get { return firstName; }
                set
                {
                    firstName = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
                }
            }
        }
        // 綁定(textBox.Text <= sourceObject.FirstName)
        Sample sourceObject = new Sample();
        textbox.DataBindings.Add("Text", sourceObject, "FirstName");
        sourceObject.FirstName = "Stack";


咱們作一下封裝,加上值相等判斷。
    定義 ViewModelBase
        using System;
        using System.ComponentModel;
        using System.Runtime.CompilerServices;
        namespace Xamarin.FormsBook.Toolkit
        {
            public class ViewModelBase : INotifyPropertyChanged
            {
                public event PropertyChangedEventHandler PropertyChanged;
                protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
                {
                    if (Object.Equals(storage, value))
                        return false;
                    storage = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    return true;
                }
            }
        }
    讓數據模型類繼承自ViewModelBase
        public class InformationViewModel : ViewModelBase
        {
            string name;
            public string Name
            {
                get { return name; }
                set 
                { 
                    if (SetProperty(ref name, value, "Name"))
                    {
                        ...屬性已經更新,該乾點啥
                    }; 
                }
            }
        }
    
 

Xamarin 的方案(使用 BindableObject)
    代碼
        public RadioButtonGroup : BindableObject
        {
            public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(Radio), "", propertyChanged: TextChanged);
            private static void TextChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var radio = (Radio)bindable;
                radio.Lbl.Text = (string)newValue;
            }
            public string Text 
            {
                get {return (string)this.GetValue(TextProperty);}
                set {this.SetValue(TextProperty, value);}
            }
        }
        this.Icon.SetBinding(Image.IsVisibleProperty, new Binding("ShowRadio", source: this));
    說明
        這裏的BindableProperty是用於描述這個屬性的元信息,如名稱、類型、默認值、綁定方向、屬性值變動事件
        BindableObject 是 INotifyPropertyChanged 的封裝,增長了綁定上下文、屬性變動中、屬性變動完畢事件等邏輯
            public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
            {
                public static readonly BindableProperty BindingContextProperty;
                public object BindingContext { get; set; }
                protected BindableObject ();
        
                // Methods
                protected void ApplyBindings (object oldContext = null);
                public void ClearValue (BindableProperty property);
                public void ClearValue (BindablePropertyKey propertyKey);
                public object GetValue (BindableProperty property);
                protected virtual void OnBindingContextChanged ();
                protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null);
                protected virtual void OnPropertyChanging ([CallerMemberName] string propertyName = null);
                public void RemoveBinding (BindableProperty property);
                public void SetBinding (BindableProperty targetProperty, BindingBase binding);
                public void SetValue (BindablePropertyKey propertyKey, object value);
                public void SetValue (BindableProperty property, object value);
                protected void UnapplyBindings ();
        
                // Events
                public event EventHandler BindingContextChanged { add; remove;}
                public event PropertyChangedEventHandler PropertyChanged {add; remove;}
                public event PropertyChangingEventHandler PropertyChanging {add; remove;}
            }
            
單向數據綁定和雙向數據綁定
    單向:model的變動會刷新view,view上作任何操做都不影響model
    雙向:model的變動會刷新view,view的變動會刷新model
    我的只喜歡用單向的,理由呢:
        喜歡實打實的代碼來控制數據刷新,雙向的數據綁定總以爲不放心
        view的交互,經常帶了邏輯控制,不只僅是給model賦值,這不是雙向數據綁定能解決的。
        


其它
    Qt QML的屬性、綁定、屬性變動事件實現起來很是優美和簡單
        A
        {
            property int Property1;
            
            // 編譯器會自動生成相似如下方法
            // bool onProperty1Changing(o, oldValue, newValue){...}
            // void onProperty1Changed(o, oldValue, newValue) {....}
        }
        b.Property2 = a.Property1;  // 綁定這兩個對象的屬性(單向綁定)
    建議 C# 優化編譯器,或者提供語法糖之類的東西(如propety關鍵字)
        public readonly property string Name;
        編譯器自動實現:
            getter、setter
            INotifyPropertyChanged 接口
            PropertyChangingEvent
            PropertyChangedEvent
            綁定邏輯
        事實上,這個功能10來年前的 Delphi 就實現了
        


什麼是 MVC
    這是將界面、邏輯、數據隔離的一種方案,這其實也是綁定的一種實現,不過是頁面級別的。
        Model      : 數據
        View       : 視圖
        Controller : 控制器
    視圖的呈現由控制器控制,視圖的數據(模型)也由控制器提供
    TODO: 補一個簡單的示例
    ASP.NET MVC
        是一個controller對應多個view
        它的controller主要就是個跳轉器,根據客戶端請求,組裝數據並提供給某個視圖,渲染到客戶端
        疑問:
            大的頁面用controller跳轉好理解
            但小的交互,如按鈕點擊頁面上的某個label變個文字,這個也交給controll是否是太瑣碎了?
            這個仍是交給頁面客戶端本身處理會更合適,但這要寫js了
    其實傳統的 Windows Form 也能夠看爲一種MVC
        View部分可由設計器建立
        View的數據展現、交互(如點擊事件)在後臺代碼中,這個能夠當作Controller
        不過這種方式是一個view對應一個controller,這個controller是這個view專用的
    ios-objectc 的MVC
        這貨和 Windows Form 差很少,也是一個視圖對應一個專用的控制器
        掛MVC的頭銜,行Windows Form的行當。

什麼是 MVVM
    這是將界面和數據徹底隔離的一種方案
        Model      : 模型
        View       : 視圖
        ViewModel  : 視圖模型
    傳統的頁面數據展現,能夠是混雜的,既有數據綁定,也能夠摻雜代碼指定。如:
    TODO:來段 MVVM 的示例代碼
    能夠看出,MVVM是一種更極端的、界面和數據徹底隔離的方案:
        它定義了一個叫 ViewModel 的東西,這個也是一種Model,但專用於視圖
        視圖的賦值只能用 ViewModel,不能用其它方式
        咱們必須爲每一個 View 定製一個 ViewModel。
    評價
        好處:徹底隔離視圖和數據、只要約定好數據接口(ViewModel)就行
        壞處:必須爲每一個視圖都定製 ViewModel,這會增長代碼量
        
        
ps
    2017-05 出了個 Xamarin Live Player 的東東,能夠在手機上展現程序運行效果
        android版本的能夠直接下載,ios版本的還在testflight中
        https://docs.microsoft.com/zh-cn/xamarin/tools/live-player/
    
相關文章
相關標籤/搜索