zoukankan      html  css  js  c++  java
  • 关于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压缩到4、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/
        
  • 相关阅读:
    day21作业
    作业day20
    K8S架构图
    day20 类的继承
    课上随手记
    第一篇CEPH集群的学习
    day19 类的讲解
    Eureka工作原理
    局域网、以太网(标准以太网、工业以太网和实时以太网)与无线局域网
    无线局域网,有线局域网
  • 原文地址:https://www.cnblogs.com/surfsky/p/9297665.html
Copyright © 2011-2022 走看看