首页 > 其他分享 >Kotlin+DataBinding的使用以及和Vue的比较

Kotlin+DataBinding的使用以及和Vue的比较

时间:2023-10-28 23:01:22浏览次数:37  
标签:... Vue name Kotlin user kotlin DataBinding android

随着移动应用前端化越来越严重,原生应用开发的比重逐渐降低,慢慢被微信小程序/ReactNative/Weex/H5+/混合应用等替代,而这些前端化的技术栈中,mvvm模式最受推崇。
google在2015年的I/O大会就推出了mvvm模式的DataBinding框架,而在实际项目中被使用的情况并不多,在前端技术快速发展的今天,mvvm模式被推向了风口浪尖,而Android的DataBinding又一次受到了关注。
在google的2017年I/O大会上,kotlin被指定为Android开发官方语言,本篇以一个小例子记录DataBinding在kotlin环境下的配置以及使用,以及和前端框架Vue的mvvm模式的比较。

Kotlin+DataBinding的使用以及和Vue的比较_android

建议以了解MVVM模式为前提阅读本篇

添加工程依赖

新建一个新的基于kotlin的Android工程后,需要在gradle的配置文件中增加一些配置和依赖才能使用DataBinding。

在项目的build.gradle文件中,先把plugin的版本号抽出来,作为一个全局变量,便于配置使用

buildscript {
    ext.kotlin_version = '1.1.51'
    ext.android_plugin_version = '3.0.1' //把plugin版本号定义一个变量
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:$android_plugin_version" //使用变量代替原版本号
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
...

在app的build.gradle文件中增加配置

...
android {
	...
    dataBinding {
        enabled = true
    }
}
dependencies {
	...
    kapt "com.android.databinding:compiler:$android_plugin_version"
}

kapt {
    generateStubs = true
}

配置完成后,这个Android工程已经支持DataBinding框架了。

XMl文件处理

为了演示方便,先调整下初始工程的MainActivity对应的布局文件activity_main.xml的内容

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    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="默认值" />
  
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

这是一个非常简单的布局,包含一个TextView和一个EditText,下面以编辑EditText的内容同步更改TextView的内容为目的来使用DataBinding对xml进行改造。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="top.nightfarmer.databindingdemo.MainActivity">
    <data>
        <variable
            name="user"
            type="top.nightfarmer.databindingdemo.User" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name, default=默认值}" />
        <EditText android:addTextChangedListener="@{user.nameWatcher}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
      </LinearLayout>
</layout>

调整到的地方有:

  • 布局文件的根标签改为了<layout>...</layout>,
  • 增加了<data>...</data>来声明DataModel变量
  • 使用@{...}语法替换布局中原有常量
  • 使用android:addTextChangedListener="@{user.nameWatcher}"进行EditText数据的双向绑定

<data>...</data>声明了一个叫做user的变量,并指定了user的类的定义,这里的user只是声明变量,并没有实例化,变量的实例化过程并不是在xml文件中,而是在绑定xml文件的代码中进行的。

定义ViewModel类

正常情况下,定义一个kotlin类是如下格式

class User {
        var name: String? = null
    }

在本次的DataBinding场景中,需要改造进行一些改造

class User {
    var name = ObservableField<String>()

    val nameWatcher = object : TextWatcher {
        override fun afterTextChanged(s: Editable) {
            if (s.toString() != name.get()) {
                name.set(s.toString())
            }
        }
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }
    }
}

这个类变得复杂了很多,name的类型改为了名为ObservableField的包装类,通过name.get和name.set方法来对name的值进行修改,达到监听的目的。
增加了名为nameWatcher的监听对象,在监听回调中,判断值的变更并对name变量进行赋值。

调整Activity

在常规模式中,我们使用setContentView(R.layout.activity_main)来指定Activity的布局文件,而使用DataBinding时,我们要使用下面的代码来替换:

val activityMainBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        activityMainBinding.user = User()

DataBinding框架根据activity_main.xml文件自动生成了名为ActivityMainBinding的绑定类,通过根据布局文件实例化的绑定类,可以给DataModel变量user进行实例化。

小结

经过上述的调整,这个小功能形成了一个完整的闭环:

  1. Activity中根据布局文件创建布局View,并通过Binding给xml中的user变量赋值
  2. 布局中通过android:text="@{user.name, default=默认值}"语法监听并更新text
  3. 布局中通过android:addTextChangedListener="@{user.nameWatcher}"监听EditText组件的内容变更,并更新user对象的name属性,name属性变更后会通知所有使用@{user.name}语法的控件进行更新

这是一个非常简单的DataBinding示例,更高级的特性不会在这里展示。
为什么呢?因为笔者并不会在实际项目中使用它,那么为什么呢?通过如两个对比来说明原因。

与Kotlin+anko对比

在app的build.gradle文件中增加anko的依赖

implementation "org.jetbrains.anko:anko:0.10.5"

xml文件保持常规写法不变
user类文件保持常规写法不变
Activity保持常规写法不变
要实现这个效果,只需要在Activity中加入5行代码

et_demo1.textChangedListener {
	onTextChanged { charSequence, _, _, _ ->
		tv_demo1.text = charSequence
	}
}

于Vue对比

或许拿Android的框架和前端框架对比并不怎么合适,但同样是MVVM模式,我或许更喜欢Vue一些?

<template>
    <div>
        <div>{{name}}</div>
        <input v-model="name"/>
    </div>
</template>

<script>
    export default {
        data: function () {
            return {
                name: '默认值'
            };
        }
    };
</script>

嗯 清爽!

总结

DataBinding的诞生或许只是为了MVVM而MVVM,也或许DataBinding是在2015年设计的所以并没有Vue这种清爽的形式。
这个示例只是实现一个简单的功能,DataBinding更高级的特性并没在这里展示,DataBinding对代码的入侵性太强,常规开发中从网络获取数据是直接反序列化为实体类并进行展示的,而DataBinding则不能正常反序列化,还需要手动转换。

总之笔者认为在项目中引入DataBinding的弊大于利,在越来越多开发者从Java阵营转入kotlin阵营的形势下,DataBinding显得较为臃肿。

在日常开发中或许kotlin+anko和ReactNative比DataBinding更加方便。

标签:...,Vue,name,Kotlin,user,kotlin,DataBinding,android
From: https://blog.51cto.com/NightFarmer/8073119

相关文章

  • web前端(Vue2.x)接收H264实时视频码流(二进制)进行播放
    1、安装 [email protected]、.vue文件中使用<template><div><videoid="dom_id"muted="muted"controlsclass="video_box"></video><divv-if="!has_data"v-loading="!......
  • vue 自定义指令
    v-if="yes"if就是指令ID,yes是expressionVue.directive(id,definition)接入两个参数,id是指令ID,definition是定义对象。定义对象可以提供一些钩子函数:bind:初始化的时候绑定inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)update:数据更新的时候......
  • vue中使用axios请求post接口为什么先发起OPTIONS请求再发起post请求?
    在使用Axios进行跨域POST请求时,浏览器会先发起一个OPTIONS请求,这是因为浏览器执行了跨域请求时的预检请求(PreflightRequest)。这是一个安全性措施,旨在确保跨域请求不会导致安全风险。1、跨域请求的安全性:当前端应用和后端API位于不同的域或端口时,浏览器会执行同源策略,以防止跨站点......
  • 手撕Vuex-实现getters方法
    经上一篇章介绍,完成了实现共享数据的功能,实现方式是在Store构造函数中将创建Store时将需要共享的数据添加到Store上面,这样将来我们就能通过this.$store拿到这个Store,既然能拿到这个Store,我们就可以通过.state拿到需要共享的属性。除了可以通过.state拿到共享数据之......
  • 手撕Vuex-实现共享数据
    经过上一篇章介绍,完成了添加全局$store,接下来就是实现共享数据的功能。在Vuex中,共享数据是通过state来实现的,所以我们需要在Nuex.js文件中实现state的功能。在Vuex中,state是一个对象,这个对象中存放的就是我们的共享数据,所以我们需要在Nuex.js文件中定义一个state......
  • vue2和vue3的区别
    vue2和vue3都是前端JavaScript框架,基本概念和功能大部分都相同,它们的区别主要在语法、原理、生态以及打包四个方面.语法:Vue.js2使用基于Object.defineProperty的双向绑定来追踪变化,而Vue.js3采用Proxy来实现响应式变化追踪,这提高了性能并允许更广泛的响应式追踪。在Vue.js3......
  • 手撕Vuex-添加全局$store
    经过上一篇的介绍,了解到了Vuex的实现本质就是一个插件,所以要做的事情就是实现这个插件的代码编写即可。本篇文章主要是实现一个全局的$store,这个$store是挂载在Vue的原型上的,所以在任何一个组件当中都可以通过this.$store访问到。我们先来看看Vue官方的,我们分别在Ap......
  • 32-Vue脚手架-Todo-list 案例
    Todo-list案例 组件化编码流程(通用)1.拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突,如下所示2.实现动态组件:要考虑好数据的存放位置,数据是要一个组件在用,还是一些组件在用一个组件在用:放在组件自身即可一些组件在用:放在他们共同的父组件上3.实现交互:从绑定......
  • Vue.js框架:vue3引入mockjs模拟数据调试
    一、引入依赖1、安装依赖包在终端中使用以下命令:npminstall@types/mockjs--save此处使用了@types进行引入,是因为在.ts文件引用包时,默认必须有类型声明,不能是any。有很多依赖包是用纯JS写的,没有类型声明。因此使用@types作为类型声明的集......
  • 手撕Vuex-Vuex实现原理分析
    本章节主要围绕着手撕Vuex,那么在手撕之前,先来回顾一下Vuex的基本使用。创建一个Vuex项目,我这里采用vue-cli创建一个项目,然后安装Vuex。vuecreatevuex-demo选择Manuallyselectfeatures。这里只需要,Babel与Vuex。选择2.X版本的Vue:创建package.json:是......