mvvm 订阅
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。 展开全文
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
信息
例    如
Silverlight、音频
外文名
Model-View-ViewModel
简    称
MVVM
隶    属
微软
MVVM优点
低耦合 可重用性
MVVM实例解析
WPF的数据绑定与Presentation Model相结合是非常好的做法,使得开发人员可以将 View和逻辑分离出来,但这种数据绑定技术非常简单实用,也是WPF所特有的,所以我们又称之为Model-View-ViewModel(MVVM)。这种模式跟经典的MVP(Model-View-Presenter)模式很相似,除了你需要一个为View量身定制的model,这个model就是ViewModel。ViewModel包含所有由UI特定的接口和属性,并由一个 ViewModel 的视图的绑定属性,并可获得二者之间的松散耦合,所以需要在ViewModel 直接更新视图中编写相应代码。数据绑定系统还支持提供了标准化的方式传输到视图的验证错误的输入的验证。在视图(View)部分,通常也就是一个Aspx页面。在以前设计模式中由于没有清晰的职责划分,UI 层经常成为逻辑层的全能代理,而后者实际上属于应用程序的其他层。MVP 里的M 其实和MVC里的M是一个,都是封装了核心数据、逻辑和功能的计算关系的模型,而V是视图(窗体),P就是封装了窗体中的所有操作、响应用户的输入输出、事件等,与MVC里的C差不多,区别是MVC是系统级架构的,而MVP是用在某个特定页面上的,也就是说MVP的灵活性要远远大于MVC,实现起来也极为简单。我们再从IView这个interface层来解析,它可以帮助我们把各类UI与逻辑层解耦,同时可以从UI层进入自动化测试(Unit/Automatic Test)并提供了入口,在以前可以由WinForm/Web Form/MFC等编写的UI是通过事件Windows消息与IView层沟通的。WPF与IView层的沟通,最佳的手段是使用Binding,当然,也可以使用事件;Presenter层要实现IView,多态机制可以保证运行时UI层显示恰当的数据。比如Binding,在程序中,你可能看到Binding的Source是某个interface类型的变量,实际上,这个interface变量引用着的对象才是真正的数据源。MVC模式大家都已经非常熟悉了,在这里我就不赘述,这些模式也是依次进化而形成MVC—>MVP—>MVVM。有一句话说的好:当物体受到接力的时候,凡是有界面的地方就是最容易被撕下来的地方。因此,IView作为公共视图接口约束(契约)的一层意思;View则能传达解耦的一层意思。 [1] 
收起全文
精华内容
参与话题
问答
  • WPF 技术主要特点是数据驱动界面 UI,当数据发生变化时,WPF 将自动通知 UI 界面同步更新,MVVM 模式充分利用了 WPF 的依赖属性、数据和命令绑定等机制,最大限度地界面显示和逻辑代码之间的耦合度,如需要调整界面...
  • mvvm个人理解

    千次阅读 2018-06-06 18:10:07
    做技术,,有什么好处么。。如果要我说的话,我之所以喜欢做技术,最大的原因是成就感吧。我喜欢做每当做完一个项目的时候的那份成就感...你有没有发现,这些框架跟jquery这些框架不一样,他们采用的都是mvvm的软件...

    做技术,,有什么好处么。。如果要我说的话,我之所以喜欢做技术,最大的原因是成就感吧。我喜欢做每当做完一个项目的时候的那份成就感。让我感觉自己的存在是有一定的价值的。但~也很累~终生学习是做技术的宿命吧。

    但是~话说回来了,哪行哪业不需要学习呢。对吧

    做前端就要不断的学习各种流行的框架,react,angular,vue。。你有没有发现,这些框架跟jquery这些框架不一样,他们采用的都是mvvm的软件架构设计模式。

    什么是mvvm呢

    简单来说,mvvm是modal-view-viewmodel的缩写。

    View层

    View层是视图层,也就是用户界面。

    Model层

    Model是指数据模型,也就是后端进行业务处理和数据处理

    ViewModel层

    ViewModel是由前端开发人员组织生成和维护的视图数据层。在这里,前端可以对从后端获取的model数据进行转换处理,以便于前端的View层使用。

    双向数据绑定和单项数据绑定对比

    <body>
    		<div class="container">
    			<p class="name">丽丽</p>
    			<p class="age">23</p>
    		</div>
    		<script type="text/javascript">
    			$(".name").html("花花");
    			$(".age").html("26")
    		</script>
    	</body>

    这时候,我们如果想改变name和age的值,就要进行dom操作,选中name和age元素,然后进行赋值操作。这是进行少量简单的改变视图操作,如果进行大量的视图绑定的数据改变的时候就会变得比较麻烦,这时候会凸显出mvvm的优势。

    下面我们用vue.js作出变化

    <div class="container">
    			<p class="name">{{user.name}}</p>
    			<p class="age">{{user.age}}</p>
    			<button @click="change">点击我</button>
    		</div>
    		<script type="text/javascript">
    				new Vue({
    					el:".container",
    					data:{
    						user:{"name":'丽丽',"age":'23'}
    					},
    					methods:{
    						change:function(){
    							this.user.name="花花";
    							this.user.age="24"
    						}
    					}
    					
    					
    				})
    		</script>

    这时候我们点击按钮只需要改变数据就能改变视图,让视图更新。所以我们前端开发者只需要关注ViewModel层,而不需要进行复杂的dom操作,真的是给我们前端开发者省去了很多很多的麻烦。

    <div class="container">
    			<input type="text" v-model="name" />
    			<p>{{name}}</p>
    		</div>
    		<script type="text/javascript">
    				new Vue({
    					el:".container",
    					data:{
    						name:"lili"
    					}
    				})
    		</script>

    双向数据绑定当我们输入框里的值改变数据也会随之改变,设想一下,如果我们提交表单时候是不是不需要在一个个的整理表单数据然后发送给后端,因为客户端输入的内容我们直接就得到了,而且是以对象的形式进行的存储,是不是可以直接发送给后端使用。是不是又给我们带来了极大的方便~~

    小伙伴们,mvvm架构设计模式是现在和以后前端流行的趋势。so~能不学么


    展开全文
  • MVVM是什么?以及MVVM优点

    千次阅读 2019-06-12 08:40:53
    MVVM 1、MVVM是什么? MVVM是Model-View-ViewModel的简写 它本质上是MVC 的改进版 MVVM(Model-View-ViewModel)框架的由来是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架...

    MVVM

    1、MVVM是什么?

    1. MVVM是Model-View-ViewModel的简写
    2. 它本质上是MVC 的改进版
    3. MVVM(Model-View-ViewModel)框架的由来是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架

    2、MVVM优点

    • MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点

    1. 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变

    2. 可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑

    3. 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码

    4. 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写

    展开全文
  • MVVM的实现原理

    万次阅读 2019-01-04 15:16:02
    1.MVVM是什么? 响应式,双向数据绑定,即MVVM。是指数据层(Model)-视图层(View)-数据视图(ViewModel)的响应式框架。它包括: 1.修改View层,Model对应数据发生变化。 2.Model数据变化,不需要查找DOM,直接...

    1.MVVM是什么?

    响应式,双向数据绑定,即MVVM。是指数据层(Model)-视图层(View)-数据视图(ViewModel)的响应式框架。它包括:

    1.修改View层,Model对应数据发生变化。

    2.Model数据变化,不需要查找DOM,直接更新View。

    2.MVVM的实现方式

    (1)发布者-订阅者模式: 一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value)

    (2)脏值检查: angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,在指定的事件触发时进入脏值检测。

    (3)数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

    3.思路

    1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者

    2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

    3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

    4、mvvm入口函数,整合以上三者

    如下图:

    在这里插入图片描述

    4.实现Observer

    写个简单的observer,监听对象的每一个属性的变化

    	const data = {
            a: 1,
            b: 2,
            c: {
                d: 4
            }
        }
        observer(data)
        data.c.d = 5
        // 观察者,遍历监听所有数据
        function observer(data) {
            if(!data || typeof data!=='object')return
            for (let key in data) {
                observeProperty(data, key, data[key])
            }
        }
        // 观察属性变化
        function observeProperty(data, key, val) {
            observer(val) // 递归监听子属性
            Object.defineProperty(data, key, {
                enumerable: true, // 可枚举
                configurable: false, // 不能再define
                get() {
                    console.log('you get it')
                    return val
                },
                set(newval) {
                    console.log('new:' + key, newval)
                    console.log('old:' + key, 1)
                    val = newval
                }
            })
        }
    

    监听到变化之后,需要实现一个消息订阅器,很简单,维护一个数组,用来收集订阅者,数据变动触发notify,再调用订阅者的update方法,代码改善之后是这样:

    // ...
    // 观察属性变化
        function observeProperty(data, key, val) {
            const dep = new Dep()
            observer(val) // 递归监听子属性
            Object.defineProperty(data, key, {
                // ...
                set(newval) {
                    if(val === newval)return
                    console.log('new:' + key, newval)
                    console.log('old:' + key, 1)
                    val = newval
                    dep.notify() // 通知所有订阅者
                }
            })
        }
    // 订阅器类
        class Dep{
            constructor(){
                this.subs = [] // 存储所有订阅者,也就是Watcher
            }
            addSub(sub){ // 新增订阅者
                this.subs.push(sub)
            }
            notify(){ // 通知所有订阅者,更新数据
                this.subs.forEach(sub=>{
                    sub.update()
                })
            }
        }
    

    通过思路图可知,这里的订阅者是指Watcher。

    // Observer.js
    Object.defineProperty(data, key, {
        // ...
        get() {
            console.log('you get it')
            // 当Dep.target不为空时,添加当前watcher, 添加完移除
            Dep.target && dep.addSub(Dep.target);
            return val
        }
    })
    
    // Watcher.js
    Watcher.prototype = {
        get(key) {
            Dep.target = this;
            this.value = data[key];    // 这里会触发属性的getter,从而添加订阅者
            Dep.target = null;
        }
    }
    

    Dep.target 表示当前正在计算的 Watcher,它是全局唯一的,因为在同一时间只能有一个 Watcher 被计算。

    5.实现Compile

    compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,如图所示:

    在这里插入图片描述

    因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中。

    class Compile {
            constructor(el, vm) {
                this.$vm = vm
                this.$el = this.isElementNode(el) ? el : document.querySelector(el)
                if (this.$el) {
                    // 把模板内容放入内存(片段)
                    this.$fragment = this.node2Fragment(this.$el)
                    // 解析模板
                    this.init()
                    // 把内存的结果返回页面
                    this.$el.appendChild(this.$fragment)
                }
            }
            init() {
                this.compile(this.$fragment)
            }
            node2Fragment(el) { // 将节点解析成片段
                const fragment = document.createDocumentFragment()
                while (child = el.firstChild) {
                    // fragment有个特点,它每appendChild一个节点,该节点在dom上被删除
                    fragment.appendChild(child)
                }
                return fragment
            }
            compile(el) {
                const childNodes = el.childNodes;
                [].slice.call(childNodes).forEach((node) => {
                    const text = node.textContent
                    const reg = /\{\{(.*)\}\}/
                    if (this.isElementNode(node)) { //属性节点
                        this.compileElement(node)
                    } else if (this.isTextNode(node) && reg.test(text)) { //文本节点
                        this.compileText(node, RegExp.$1)
                    }
                    // 递归遍历编译子节点
                    if (node.childNodes && node.childNodes.length) {
                        this.compile(node)
                    }
                })
            }
            compileElement(node) {
                const nodeAttrs = node.attributes;
                [].slice.call(nodeAttrs).forEach(attr => {
                    // 规定:指令以 v-xxx 命名
                    // 如 <span v-text="content1"></span> 中指令为 v-text
                    const attrName = attr.name // v-text
                    if (this.isDerective(attrName)) {
                        const exp = attr.value // content1
                        const dir = attrName.substring(2) // text
                        if (this.isEventDirective(dir)) {
                            // 事件指令, 如 v-on:click
                            compileUtil.eventHandler(node, this.$vm, exp, dir)
                        } else {
                            // 普通指令
                            compileUtil[dir] && compileUtil[dir](node, this.$vm, exp)
                        }
                    }
                })
            }
            compileText(node, exp) {
                compileUtil.text(node, this.vm, exp)
            }
            isElementNode(node) {
                return node.nodeType === 1
            }
            isTextNode(node) {
                return node.nodeType === 3
            }
            isDerective(attrName) {
                return attrName.indexOf('v-') >= 0
            }
            isEventDirective(attrName) {
                return attrName.indexOf('on') >= 0
            }
        }
    

    compile方法将遍历所有节点及其子节点,进行扫描解析编译,调用对应的指令渲染函数进行数据渲染,并调用对应的指令更新函数进行绑定。本文举text,model和事件注册三个例子来说明,更新函数代码如下:

    const compileUtil = {
            text(node, vm, exp) {
                this.bind(node, vm, exp, 'text')
            },
            model(node, vm, exp) {
                this.bind(node, vm, exp, 'model')
                node.addEventListener('input',e=>{
                    // 视图的变化更新到vm实例
                    vm.$data[exp]= e.target.value
                })
            },
            bind(node, vm, exp, dir) {
                const updaterFn = updater[dir + 'Updater']
                // 初始化视图
                updaterFn && updaterFn(node, vm[exp])
                // 实例化订阅者,此操作会在对应的属性消息订阅器中添加该订阅者watcher
                new Watcher(vm, exp, (value, oldValue) => {
                    updaterFn && updaterFn(node, value, oldValue)
                })
            },
            // 注册事件
            eventHandler(node,vm,exp,dir){
                // dir  on:click
                const eventType = dir.split(':')[1]
                const fn = vm.$options.methods && vm.$options.methods[exp]
                if(eventType&&fn){
                    node.addEventListener(eventType,fn.bind(vm))
                }
            }
        }
        // 更新函数
        const updater = {
            textUpdater(node, value) {
                node.textContent = typeof value === 'undefined' ? '' : value
            },
            modelUpdater(node, value) {
                node.value = typeof value === 'undefined' ? '' : value
            }
        }
    

    至此,视图和数据已经绑定成功,页面的初始化渲染由vm实例的属性初始化完成,并对节点上的事件进行了注册,接下了就是Watcher的事了。它将接受数据的变化,并重新更新视图。

    其中,双向绑定由v-model实现,初始化时调用modelUpdater对input中的value初始化赋值,然后添加input事件监听,当视图层的数据变化后,更新vm实例上对应属性的值。

    6.实现Watcher

    Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:

    1、在自身实例化时往属性订阅器(dep)里面添加自己

    2、自身必须有一个update()方法

    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

    class Watcher {
            constructor(vm, exp, cb) {
                this.vm = vm
                this.exp = exp
                this.cb = cb
                this.value = this.get() // 缓存当前值(旧值)
            }
            get() {
                Dep.target = this // 将当前订阅者指向自己
                const value = this.vm[this.exp] // 触发getter,添加自己到属性订阅器中
                Dep.target = null // 添加完毕,重置
                return value
            }
            update() {
                this.run()
            }
            run() {
                const value = this.get() // 获取新值
                const oldValue = this.value
                if (value !== oldValue) {
                    this.value = value
                    this.cb.call(this.vm, value, oldValue) // 执行Compile中绑定的回调,更新视图
                }
            }
        }
    
    // 这里再次列出Observer和Dep,方便理解
        Object.defineProperty(data, key, {
            get() {
                Dep.target && dep.addSub(Dep.target)
                return val
            }
        })
        Dep.prototype = {
            notify() {
                this.sbus.forEach(sub => sub.update())
            }
        }
    

    实例化Watcher的时候,调用get()方法,通过Dep.target = watcherInstance标记订阅者是当前watcher实例,强行触发属性定义的getter方法,getter方法执行的时候,就会在属性的订阅器dep添加当前watcher实例,从而在属性值有变化的时候,watcherInstance就能收到更新通知。

    7.实现MVVM

    MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

    class MVVM {
            constructor(options) {
                this.$options = options
                this.$el = options.el
                this.$data = options.data
                // 属性代理,实现 vm.xxx -> vm._data.xxx
                for (let key in this.$data) {
                    this.$proxy(key)
                }
                observer(this.$data)
                this.$compile = new Compile(this.$el || document.body, this)
            }
            $proxy(key) {
                Object.defineProperty(this, key, {
                    configurable: false,
                    enumerable: true,
                    get() {
                        return this.$data[key]
                    },
                    set(newVal) {
                        this.$data[key] = newVal
                    }
                })
            }
        }
    

    参考文章:

    Vue实现双向数据绑定

    手写MVVM

    Object的defineProperty

    展开全文
  • MVVM简介与运用

    万次阅读 2016-08-01 16:06:22
    在介绍MVVM框架之前,先给大家简单介绍一下MVC、MVP框架(由于本博文主要讲解MVVM,所以MVC和MVP将简化介绍,如果需要我将在以后的博文中补充进来)。 MVC框架: M-Model : 业务逻辑和实体模型(biz/bean) V-...

    前言

    在介绍MVVM框架之前,先给大家简单介绍一下MVC、MVP框架(由于本博文主要讲解MVVM,所以MVC和MVP将简化介绍,如果需要我将在以后的博文中补充进来)。

    MVC框架:

    • M-Model : 业务逻辑和实体模型(biz/bean)
    • V-View : 布局文件(XML)
    • C-Controller : 控制器(Activity)

    相信大家都熟悉这个框架,这个也是初学者最常用的框架,该框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加,你就会没办法忍受该框架开发的项目,这时MVP框架就应运而生。

    MVP框架:

    • M-Model : 业务逻辑和实体模型(biz/bean)
    • V-View : 布局文件(XML)和Activity
    • P-Presenter : 完成View和Model的交互

    MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,为了复用和可拓展性,MVP框架基于接口设计的理念大家自然就可以理解其用意。

    但MVP框架也有不足之处:

    1.接口过多,一定程度影响了编码效率。

    2.业务逻辑抽象到Presenter中,较为复杂的界面Activity代码量依然会很多。

    3.导致Presenter的代码量过大。

     

    MVVM框架:

    • M-Model : 实体模型(biz/bean)
    • V-View : 布局文件(XML)
    • VM-ViewModel : DataBinding所在之处,对外暴露出公共属性,View和Model的绑定器

    对比MVP和MVVM模型图可以看出,他们之间区别主要体现在以下两点:


    1.    可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。 在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。


    2.    低耦合。以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面) 甚至都不需要再Activity、Fragment去findViewById()。这时候Activity、Fragment只需要做好的逻辑处理就可以了。


    说了这么多理论知识,相信大家都有所厌烦了,下面就不来“虚”的了直接来“干”的,大家可能会问MVVM框架在Android怎么样使用?

    Google在2015年的已经为我们提供DataBinding技术,以便让我们快速实现MVVM框架的实现。下面就详细讲解如何使用DataBinding?


    由于本人使用的是AndroidStudio(以下简称AS),所以接下来都是关于AS相关使用规则:

    1.检查你的AS版本,要求在1.3.0以上

    2.Gradle 版本1.3.0-beta4以上

    3.在工程根目录build.gradle文件加入如下配置:

    dependencies {
          classpath "com.android.tools.build:gradle:1.3.0-beta4"
          classpath "com.android.databinding:dataBinder:1.0-rc1"
       }

    allprojects {
       repositories {
           jcenter()
       }
    }

    4.在app里的build.gradle文件加入如下配置:

    apply plugin: 'com.android.application'
    apply plugin: 'com.android.databinding'


    下面来比较一下布局与之前大家常用的格式的区别:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"/>
       </LinearLayout>
    </layout>

    l  先将根布局改为layout

    l  在布局里引入的model 中的数据类:

    <variable name="user" type="com.example.User"/>(还有一种写法将在后面代码中介绍)

    l  设置布局属性值,通过@{}语法:

    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="@{user.firstName}"/>


    数据实体类User:

    public class User {
       private final String firstName;
       private final String lastName;
       public User(String firstName, String lastName) {
           this.firstName = firstName;
           this.lastName = lastName;
       }
       public String getFirstName() {
           return this.firstName;
       }
       public String getLastName() {
           return this.lastName;
       }
    }


    在Activity中进行数据的绑定:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
       User user = new User("Test", "User");
       binding.setUser(user);
    }


    这时你运行程序就会看到在界面上会显示你设置的测试用户数据,当然你还可以这样做去获取binding:

    MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

    如果你使用的是ListView或者RecyclerView去显示界面,这时候在Items布局中使用Data Binding,在Adapter中你可以这样获取binding:

    ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
    //or
    ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);


    接下来就是事件的实现,在我们以往的使用中对于事件的实现都是android:onClick或者在代码中使用View.setOnClickListener()来实现点击事件,在这里将有一种新的实现方式:

    要将事件分配给它的处理程序,使用一个正常的绑定表达式,以值作为调用的方法名称。例如:你的数据对象有两种方法

    public class MyHandlers {
        public void onClickFriend(View view) { ... }
        public void onClickEnemy(View view) { ... }
    }


    绑定表达式可以为视图指定单击事件监听器

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="handlers" type="com.example.Handlers"/>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"
               android:onClick="@{user.isFriend ?handlers.onClickFriend :handlers.onClickEnemy}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"
               android:onClick="@{user.isFriend ?handlers.onClickFriend :handlers.onClickEnemy}"/>
       </LinearLayout>
    </layout>


    由于DataBinding对布局使用改动比较大,下面主要讲解一下布局:


    ①在布局中import导入

    <data>
        <import type="android.view.View"/>
    </data>

     

    之后在你的布局中通过View控件特性进行对其实现类隐藏和显示操作

    <TextView
       android:text="@{user.lastName}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

    可能有人会问如果我导入的类与已导入的类的名字冲突怎么办?那么接下来就会解决这个问题!

    <import type="android.view.View"/>
    <import type="com.example.real.estate.View"
            alias="Vista"/>

    这里的“alias”属性就是别名的意思,你可以采用别名的方式解决这个问题。


    ②当你在布局中引用的变量是一个List集合,需将集合的左”<”使用转义字符输入,如下(想要了解转义字符具体表达形式,请自行查询):

    <data>
        <import type="com.example.User"/>
        <import type="java.util.List"/>
        <variable name="user" type="User"/>
        <variable name="userList" type="List&lt;User>"/>
    </data>


    ③类型转换

    <TextView
       android:text="@{((User)(user.connection)).lastName}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>


    ④当导入的类中存在静态属性和方法时,你也是可以在布局中直接使用

    <data>
        <import type="com.example.MyStringUtils"/>
        <variable name="user" type="com.example.User"/>
    </data><TextView
       android:text="@{MyStringUtils.capitalize(user.lastName)}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>


    ⑤属性Variables:

    在数据元素中可以使用任意数量的变量元素。每个可变元素描述一个属性,该属性可以设置在布局文件中的绑定表达式中使用的布局上:

    <data>
        <import type="android.graphics.drawable.Drawable"/>
        <variable name="user"  type="com.example.User"/>
        <variable name="image" type="Drawable"/>
        <variable name="note"  type="String"/>
    </data>


    ⑥自定义Binding类的类名:

    <data class="CustomBinding"></data> 在app_package/databinding下生成CustomBinding;

    <data class=".CustomBinding"></data> 在app_package下生成CustomBinding;

    <dataclass="com.example.CustomBinding"></data> 明确指定包名和类名。


    ⑦include使用

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:bind="http://schemas.android.com/apk/res-auto">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <include layout="@layout/name"
               bind:user="@{user}"/>
           <include layout="@layout/contact"
               bind:user="@{user}"/>
       </LinearLayout>
    </layout>


    ⑧DataBinding数据绑定不支持包括合并元素的直接子元素,例如下面的写法是不被允许的:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:bind="http://schemas.android.com/apk/res-auto">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <merge>
           <include layout="@layout/name"
               bind:user="@{user}"/>
           <include layout="@layout/contact"
               bind:user="@{user}"/>
       </merge>
    </layout>

    注意:name.xml 和 contact.xml都必须包含 <variable name="user" ../>

    DataBinding支持的表达式有:

    数学表达式: + - / *%

    字符串拼接 +

    逻辑表达式&& ||

    位操作符 & | ^

    一元操作符 + - ! ~

    位移操作符 >>>>> <<

    比较操作符 == >< >= <=

    instanceof

    分组操作符 ()

    字面量 -character, String, numeric, null

    强转、方法调用

    字段访问

    数组访问 []

    三元操作符 ?

    聚合判断(Null Coalescing Operator)语法 ‘??’

    例如:

    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'

    android:text="@{user.displayName ?? user.lastName}"

    上面代码的意思是如果displayName为null,则显示lastName,否则显示displayName;

    android:text="@{user.displayName != null ? user.displayName : user.lastName}"


    集合Collections

    <data>
        <import type="android.util.SparseArray"/>
        <import type="java.util.Map"/>
        <import type="java.util.List"/>
        <variable name="list" type="List&lt;String>"/>
        <variable name="sparse" type="SparseArray&lt;String>"/>
        <variable name="map" type="Map&lt;String, String>"/>
        <variable name="index" type="int"/>
        <variable name="key" type="String"/>
    </data>
    …
    android:text="@{list[index]}"
    …
    android:text="@{sparse[index]}"
    …
    android:text="@{map[key]}"


    ①String literals(字符串常量)

    当使用单引号围绕属性值时,在表达式中使用双引号是很容易的:

    android:text='@{map["firstName"]}'


    ②也可以使用双引号来环绕属性值。当你这样做的时候,String literals要么用&quot;或反引号(`):

    android:text="@{map[`firstName`}"
    android:text="@{map[&quot;firstName&quot;]}"


    Resources资源:

    在DataBinding语法中,可以把resource作为其中的一部分:

    android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

    除了支持dimen,还支持color、string、drawable、anim等。

    注意:对mipmap图片资源支持还是有问题,目前只支持drawable。


    一些资源要求需明确的类型赋值:

    Type

    Normal Reference

    Expression Reference

    String[]

    @array

    @stringArray

    int[]

    @array

    @intArray

    TypedArray

    @array

    @typedArray

    Animator

    @animator

    @animator

    StateListAnimator

    @animator

    @stateListAnimator

    Color   int

    @color

    @color

    ColorStateList

    @color

    @colorStateList

    到这里基本的属性使用方法介绍就结束了,下面大家应该对数据变化时,UI如何呈现很迷惑,好了,现在开始讲数据的变化如何让UI的更新?

    任何普通的java对象(POJO)可用于数据绑定,但修改一个POJO不会造成UI更新。数据绑定(DataBinding)的真正力量可以通过给你的数据对象在数据改变时通知你来使用。有三种不同的数据变化通知机制,Observable objectsobservable fields, and observable collections。

    下面来逐个讲解:

    Observable Objects

    一个实现可观察到的接口的类,将允许绑定到绑定一个单一的监听器绑定到一个绑定对象,以监听该对象上的所有属性的更改;观察到的接口有一个机制来添加和删除监听器,但通知是由开发人员来进行的。为使开发更容易,一个基类,baseobservable,是为了实现监听器注册机制。数据类实现者仍然是负责通知时的性能变化。这是通过分配一个绑定注释getter和setter进行通知:

    private static class User extends BaseObservable {
       private String firstName;
       private String lastName;
       @Bindable
       public String getFirstName() {
           return this.firstName;
       }
       @Bindable
       public String getLastName() {
           return this.lastName;
       }
       public void setFirstName(String firstName) {
           this.firstName = firstName;
           notifyPropertyChanged(BR.firstName);
       }
       public void setLastName(String lastName) {
           this.lastName = lastName;
           notifyPropertyChanged(BR.lastName);
       }
    }

    注意:BR类自动生成的

    好了,现在你就会发现当通过set方法改变数据后,UI就会自动更新!

     

    ObservableFields

    private static class User {
       public final ObservableField<String> firstName = new ObservableField<>();
       public final ObservableField<String> lastName = new ObservableField<>();
       public final ObservableInt age = new ObservableInt();
    }

    在代码中设置数据:

    user.firstName.set("Google");
    int age = user.age.get();


    Observable Collections

    ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
    user.put("firstName", "Google");
    user.put("lastName", "Inc.");
    user.put("age", 17);

    布局中使用:

    <data>
        <import type="android.databinding.ObservableMap"/>
        <variable name="user" type="ObservableMap&lt;String, Object>"/>
    </data><TextView
       android:text='@{user["lastName"]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user["age"])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>


    如果集合的key是Integer,可以使用ObservableArrayList代替ObservableArrayMap

    ObservableArrayList<Object> user = new ObservableArrayList<>();
    user.add("Google");
    user.add("Inc.");
    user.add(17);


    在布局中使用:

    <data>
        <import type="android.databinding.ObservableList"/>
        <import type="com.example.my.app.Fields"/>
        <variable name="user" type="ObservableList&lt;Object>"/>
    </data><TextView
       android:text='@{user[Fields.LAST_NAME]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

    文章写到这里,DataBinding的一些基本用法已经介绍完毕,如果您想要了解更加详细、全面的内容,请自行去sdk的Doc文档中阅读或者关注我以后的博文。


    第一次写博客,两天的奋斗,希望大家多多指点,有可能不够全面,由于项目工期压缩,没那么多时间详细介绍,希望大家可以多交流!


    参考文献:


    官方文档:

    SDK/docs/tools/data-binding/guide.html

    CSDN博客:

    http://blog.csdn.net/qq_17250009/article/details/51161074

    http://blog.csdn.net/johnny901114/article/details/50706329

    展开全文
  • MVVM

    千次阅读 2016-12-18 13:13:43
    目的:便于开发和维护代码M(Model):数据模型 V(View + Controller): 展示内容 + 如何展示 VM(ViewModel):视图模型,处理展示的业务逻辑,包括按钮的点击,数据的请求和解析登录功能实现MVC实现: ...
  • wpf--->MVVM

    2020-11-13 16:59:09
    MVVM适合制作企业级的应用 文章目录MVVM优势什么是MVVMModelView&ViewModel MVVM优势 团队层面:统一团队的思维方式和实现方法(特定的代码放在特定的位置) 架构层面:稳定,解耦合 代码层面:可读,可测,可...
  • Mvvm定义 MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。【模型】指的是后端传递的数据。【视图】指的是所看到的页面。【视图模型】mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将...
  • MVVM

    2020-01-04 14:20:05
    Model–View–ViewModel(MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 ...
  • MVVM

    2019-11-11 16:36:51
    前后台分离开发中的前端思想 MVVM原理
  • 什么是MVVM

    2020-05-12 15:36:14
    MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View ...
  • MVC、MVP、MVVM,我到底该怎么选?

    万次阅读 多人点赞 2018-07-03 20:42:29
    本文由玉刚说写作平台提供写作赞助 原作者:AndroFarmer 版权声明:本文版权归微信...比如看了好多篇文章都搞不懂MVC到底是个啥本来想写个MVP写着写着就变成MVC了,到底Databing和MVVM之间有啥见不得人的关系...
  • Android高精战争(MVC、MVP、MVVM

    千次阅读 2018-01-05 08:56:56
    和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于App的架构如何设计: 我的App需要应用这些设计架构吗? MVC,MVP等架构讲的是什么?区别是什么? ...本文就来带你分析一下这...
  • MVVM

    2020-02-06 20:09:31
    MVC架构 M 层 ( Model ) 模型 V 层 ( View ) 视图 C 层 ( Controller ) 控制器 MVP架构 M 层 ( Model ) 模型 ...MVVM架构 M 层 ( Model ) 模型 V 层 ( View ) 视图 VM...
  • MVVM

    2019-05-06 01:21:17
    jquery实现to-do-list <div> <input type="text" name="" id="txt-title" /> <button id="btn-submit">submit</button> </div> <div> <ul id="ul-list">...<...
  • MVVM

    2019-01-15 10:46:58
    MVVM框架Vue.js的数据驱动就是通过MVVM这种框架来实现的。MVVM框架主要包含3个部分:model、view和 viewmodel。 Model:指的是数据部分,对应到前端相当于javascript对象;View:指的是视图部分,对应前端相当于dom; ...
  • 全面介绍Android的MVVM框架 - 数据绑定

    万次阅读 多人点赞 2019-12-05 17:00:09
    本教程是跟着 Data Binding Guide 学习过程中得出的一些实践经验,...Data Binding 解决了 Android UI 编程中的一个痛点,官方原生支持 MVVM 模型可以让我们在不改变既有代码框架的前提下,非常容易地使用这些新特性。
  • MVVM

    2016-06-22 11:31:00
    针对MVVM,网上有很多不错的文章,比如MVVM介绍、被误解的 MVC 和被神化的 MVVM以及Look at MVVM from a different perspective等等 文章前我想先提几个问题 MVVM到底是什么?它和MVC有什么区别? MVVM中VM到底...
  • Android MVVM 架构 Data Binding 的使用

    万次阅读 多人点赞 2016-02-20 21:57:05
    项目整体效果: 什么是MVVM , 为什么需要MVVMMVVM是Model-View-ViewModel的简写. 它是有三个部分组成:Model、View、ViewModel。Model:数据模型层。包含业务逻辑和校验逻辑。View:屏幕上显示的UI界面(layout、...
  • 分享一个我开发的MVVM架构的开源小项目

    万次阅读 多人点赞 2019-03-01 07:56:31
    大家好,今天跟大家分享一个我编写的MVVM架构的开源小项目。话说这个小项目已经提前跟大家预热很久了,也是被不少朋友催了很多次。我之前在公众号里透漏过这个项目能够帮助大家更好地理解MVVM架构,当然我也希望确实...

空空如也

1 2 3 4 5 ... 20
收藏数 57,998
精华内容 23,199
关键字:

mvvm