关于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/
    
相关文章
相关标签/搜索