Kotlin-DataBinding基础入门

DataBinding 数据绑定。是Google对MVVM在Android上的一种实现。能够直接绑定数据到xml中。
dataBinding:官方文档 (需要梯子)
我的开发环境:jdk8,AndroidStudio3.4(中文社区),gradle插件5.1.1,Kotlin插件1.3.40

Android Studio 2.0以前databinding需要通过添加依赖库com.android.databinding,之后被内置。
之前的初始配置(在module的build.gradle中):

apply plugin: 'kotlin-kapt'
android {
    ...
    dataBinding {
        enabled true
    }
}
dependencies{
    kapt "com.android.databinding:compiler:3.1.2"
}

所以之后的环境配置(在module的build.gradle中):

android {
    ...
    //开启dataBinding
    dataBinding {
        enabled  true
    }
}

开始使用

  1. 数据类
data class DataBean(
    var name: String,
    var age: Int,
    var isShow: Boolean
)
  1. 创建布局
    需要在原来的布局外面嵌套一层<layout>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <!--数据绑定-->
    <data>
        <!--name值自定义,type要绑定的数据类-->
        <variable name="user" type="com.zxx.zmvvm.service.data.DataBean" />
        <!--也可以不必用数据类-->
        <variable name="otherData" type="String" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:text="@{user.name}" />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:text="@{String.valueOf(user.age)}" />

    </LinearLayout>
</layout>

需要注意:

  • xml最外层需要嵌套一层<layout>
  • 如果想要绑定数据在通过<data>的<variable>属性绑定,绑定后还可以通过variable的name属性使用数据
  1. 绑定Variable
    PS:在绑定数据之前需要make以下工程,让其生成Binding类
    生成的databinding.png

    生成规则:
  • 默认生成规则:xml通过文件名称生成,使用下划线切割大写和小写。比如我的activity_main.xml就生成ActivityMainBinding
  • view的生成规则相似,如TextView的id 是tv_name,则生成tvName
  • 如果想自定义生成的class名:
<data class=“DefalutClass”>
…
</data>

全部Binding实例的生成都能够通过DataBindingUtil进行,方法名与该view的原inflate方法一致。
所以在Activity中绑定Variable:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //这个不用了,要通过下面的方法来绑定视图
        //setContentView(R.layout.activity_main)

        //布局编辑好后需要make Project
        //布局绑定从setContentView( laytouId )换成了 DataBindingUtil.setContentView( activity, layoutId)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        //直接给数据赋值(此处的user就是布局里variable的name属性)
        binding.user = DataBean("Zxx", 18, false)

        //----------------------------------------------
        //或者通过setVariable
        val bean = DataBean("Zxx", 18, false)
        //BR是make生成的类
        binding.setVariable(BR.user, bean)

        //-----------------------------------------------
        //或者绑定视图 测试没有显示出来
        binding.tvName.text = "ZZZ"
        binding.tvAge.setText("20")
        //但是打印正确
        Log.e("-Tag-", binding.tvName.text as String)

        //----------------------------------------------
        //最初不用databinding,是通过Kotlin插件导入id来实现的
        tv_name.text = "Zxx"  // 使用了kotlin-android-extensions
    }
}

在Fragment中绑定variable:

class UserFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        //把原来的inflater.inflate换成了DataBindingUtil.inflate
        val binding = DataBindingUtil.inflate<FragmentUserBinding>(
            inflater, R.layout.fragment_user, container, false)

        //或者
//        val view = inflater.inflate(R.layout.fragment_user, container, false)
//        val binding = DataBindingUtil.bind<FragmentUserBindingImpl>(view)

        binding?.viewModel = UserViewModel(context!!)
        return binding?.root
    }
}

事件绑定

· 方法引用:
   直接在xml布局中引入android:onClick,android:onLongClick,android:onTextChanged;注意:方法签名需和相应listener方法一致 且方法里面需要参数view。
调用方式为variable.method或者variable::method

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>   
        <!--在这里定义方法通过activity引用,其实任何类都行-->
        <variable name="activity" type="com.zxx.zmvvm.MainActivity" />
        <!--通过presenter引用方法-->
        <variable name="presenter" type="com.zxx.zmvvm.presenter.MainPresenter" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!--调用方式为variable.method或者variable::method-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{activity::onClick}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{activity.onClickTwo}"/>

        <!--方法名随意-->
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="输入信息"
            android:onTextChanged="@{presenter::onTextChanged}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{presenter.onClickTwo}"/>
    </LinearLayout>
</layout>

代码中:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        ...
        //为了绑定方法
        binding.activity = this
        binding.presenter = MainPresenter()
    }

     //注意:方法里面需要参数view
    fun onClick(view: View) {
        Toast.makeText(this, "hello", Toast.LENGTH_SHORT).show()
    }

    fun onClickTwo(view: View) {
        Toast.makeText(this, "world", Toast.LENGTH_SHORT).show()
    }
}
class MainPresenter {

    fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        Log.e("-Tag-", s.toString())
    }


    fun onClickTwo(view: View) {
        Log.e("-Tag-", "hello world")
    }
}

· 监听绑定(lambda):
   监听绑定可以使用复杂的参数。

<TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{user.isShow ? View.GONE : View.VISIBLE}"
            android:onClick="@{()->presenter.onParameterListener(user)}"/>
class MainPresenter {
    fun onParameterListener(user: DataBean) {
        Log.e("-Tag-", user.name)
    }
}

两者的区别主要是方法引用会在绑定数据时创建好View的onClickListener,而绑定监听在事件发生时才会创建。

DataBinding表达式:

与我们平常用的表达式基本相同,只是这是在xml里用

  • 算术表达式 + - / * %
  • 字符串合并 +
  • 逻辑运算 && ||
  • 二元表达式 & | ^
  • 一元表达式 + - ! ~
  • 二进制移位 >> >>> <<
  • 比较 == > < >= <=
  • instanceof
  • Grouping ()
  • 文字类型 - character, String, numeric, null
  • 强转 Cast
  • 方法调用
  • 属性引用
  • 数组 [] (须要注意的是数组的越界)
  • 三元 ?:
  • 空合并 ?? (如 a??b 意思是如果a不为空选a,如果为空选b)
    尚且不支持this, super, new, 以及显示的泛型调用。

绑定更新:

  数据类更新后并不会更新UI,而数据绑定后,我们当然会希望数据变更UI也会即时刷新。这时就要用到观察者Observable
1. BaseObservable
主要针对数据类的,把我们写的实体类直接继承BaseObservable.

class UserBean : BaseObservable() {
    @get:Bindable
    var firstName: String? = null
        set(firstName) {
            field = firstName
            // 每当值set()后,通过notifyPropertyChanged()方法去指定更新
            // 可更新某个值,可以更新整个数据,取决于你BR后面的属性
            // BR._all 可更新所有的BR中字段相关联的UI
            //这里只更新firstName属性
            notifyPropertyChanged(BR.firstName)
        }
    @get:Bindable
    var lastName: String? = null
        set(lastName) {
            field = lastName
            notifyPropertyChanged(BR.lastName)
        }
}

用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry,为了更新我们还是需要通过notify发出通知。BaseObservable提供了notifyChange()notifyPropertyChanged(int)方法,前者会刷新全部的值域,后者则仅仅更新相应BR的flag。所以通过调用notifyPropertyChanged(BR.firstName)来通知系统 BR.firstName 这个 entry 的数据已经发生变化,需要更新 UI。

2. Observable Fields:
这个主要是观察具体字段值,提供的方法有:ObservableBoolean,ObservableByte,ObservableChar,ObservableDouble,ObservableField<T>,ObservableFloat,ObservableInt,ObservableLong,ObservableShort,ViewDataBindingObservableParcelable<T extends Parcelable>

class User(){
    var firstName: ObservableField<String> = ObservableField()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}
<!--更新绑定-->
<variable name="userField" type="com.zxx.zmvvm.service.data.User" />
//使用
binding.userField?.firstName?.set("zzz")
var name = binding.userField?.firstName?.get()

或者直接在布局里声明

<!--更新绑定,其中&lt;是小于号-->
<import type="androidx.databinding.ObservableField" />
<variable name="title" type="ObservableField&lt;String>" />
private val title = ObservableField("defaultTitle")

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   //布局编辑好后需要make Project
    //布局绑定从setContentView( laytouId )换成了 DataBindingUtil.setContentView( activity, layoutId)
   val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

   binding.title = title
}

//注意:方法里面需要参数view
fun onClick(view: View) {
    title.set("更新绑定")
}

通过该字段的 set(value) /get() 方法来更新值/获取值。

  1. Observable Collections:
    如果想动态的保存数据,这时候我们会希望使用Map来存储数据结构。Observable提供了ObservableArrayMap()
var userMap: ObservableArrayMap<String, Any> = ObservableArrayMap()
userMap["name"] = "Zxx"
userMap.put("age", 12)
userMap.put("isShow", true)

<!--集合-->
<import type="androidx.databinding.ObservableMap" />
<variable name="user" type="ObservableMap&lt;String,Object>" />

<TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text='@{userMap["name"]}'/>

也提供ObservableArrayList()

var userList = ObservableArrayList<Any>()
userList.add("Zxx")
userList.add(18)
userList.add(true)

//layout中直接通过数字下标进行訪问。

参考:
https://www.cnblogs.com/zhchoutai/p/8431033.html
MVVM与DataBinding

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容