Android Jetpack DataBinding入门填坑(三)

Jetpack系列
Android Jetpack WorkManager初级认识
Android Jetpack ViewModel由浅入深

附上官网地址https://developer.android.com/topic/libraries/data-binding/expressions

一、DataBinding的定义及作用

是一个支持库,使用该库,您可以使用声明性格式而非编程方式将布局中的UI组件绑定到应用程序中的数据源。不需要在使用findViewById之类的,布局文件也不再简单的是一个布局文件,可以包含数据以及逻辑。

配置必要信息

Build.gradle文件添加下面信息

android {
    ...
    dataBinding {
        enabled = true
    }
}

二、基本使用

1. 简单展示绑定过程
  1. 新建activity_databinding.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
  // 定义数据的标签
    <data>
        <variable
            name="title"
            type="String" />
    </data>
 // 定义布局的标签
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="@{title}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

上面的格式:<layout></layout>包含两个子标签,

  1. <data></data>定义我们的数据的地方,
  2. <ConstraintLayout></ConstraintLayout>,这个布局就相当于我们平时的布局。
    TextViewtext绑定了android:text="@{title}",如果第一次设置title数据变化,页面就会跟着变化。

这里解释一下<variable/>标签中的属性,
name是定义的变量名
type是变量的类型

  1. 新建Acitivity绑定布局
class DatabasingActivity : AppCompatActivity() , View.OnClickListener {


    lateinit var mactivityBinding: ActivityDatabindingBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //绑定布局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        mactivityBinding.title = "基础使用"
    }
}

上面的ActivityDatabindingBinding是系统自动帮我们生成的,使用DataBindingUtil.setContentView(this,R.layout.activity_databinding)便可以绑定好布局,(不需要像以前调用setContentView()),mactivityBinding.title = "基础使用"赋值给title,textView就会自动展示出数据了。

注意点:

  1. ActivityDatabindingBinding是根据你的布局.xml去生成的 这里的是activity_databinding.xml所以生成ActivityDatabindingBinding.
  2. 如果发现databinding生成的ActivityDatabindingBindingImpl(看自己的命名)中出现路径报错,那么最大的可能性是你的.xml文件里面写错了之类的!!!

下面看一下.xml文件支持什么表达式。

三、支持的表达式:

  • 数学计算符 + - / * %
  • 字符连接符号 +
  • 逻辑运行符号 && ||
  • 二元运行符号 & | ^
  • 一元运行符号 + - ! ~
  • 移位 >> >>> <<
  • 比较符号 == > < >= <=(请注意,<必须转义为&lt;)
  • instanceof
  • Grouping ()
  • character, String, numeric, null
  • cast
  • 方法调用
  • 现场访问
  • Array存取 []
  • 三元运算符 ?:

四、 不支持的表达式:

  • this
  • super
  • new
  • explicit generic invocation (显式泛型调用)

五、 设置点击事件跟导入数据类

  1. 新建数据实体类
data class Person(var Name:String,var Age:Int)
  1. 修改一下布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
      //关键点1
        <import type="testview.zhen.com.myapplication.bean.Person"/>
       //关键点2
        <variable
            name="peronInfo"
            type="Person" />
     //关键点3
        <variable
            name="onClickListener"
            type="android.view.View.OnClickListener" />
        <variable
            name="title"
            type="String" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="172dp"
        android:text="@{peronInfo.name,default=预览}"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="-16dp" />
//关键点4
    <TextView
        android:id="@+id/textView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{String.valueOf(peronInfo.age),default=预览}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_view" />
//关键点4
    <Button
        android:id="@+id/button7"
        android:layout_width="159dp"
        android:layout_height="46dp"
        android:layout_marginTop="56dp"
        android:onClick="@{onClickListener}"
        android:text="点击1"
        app:layout_constraintEnd_toStartOf="@+id/button8"
        app:layout_constraintHorizontal_bias="0.451"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <Button
        android:id="@+id/button8"
        android:layout_width="159dp"
        android:layout_height="46dp"
        android:layout_marginTop="56dp"
        android:layout_marginEnd="36dp"
        android:onClick="@{onClickListener}"
        android:text="点击2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

关键代码1:<import type="testview.zhen.com.myapplication.bean.Person"/> 导入数据类所在的路径包名,要导入后才能使用
关键代码2:定义Person类型的变量
关键代码3:定义android.view.View.OnClickListenerView的点击事件
关键代码4:在Buttonandroid:onClick="@{onClickListener}中引用关键代码3定义的点击事件
3 修改一下Activity文件

class DatabasingActivity : AppCompatActivity() , View.OnClickListener {


    lateinit var mactivityBinding: ActivityDatabindingBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //绑定布局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        //赋值给布局中定义的<data>中的面的peronInfo
        mactivityBinding.peronInfo = Person("man",18)
        //实现布局中定义的<data>中的onClickListener变量,这里传入this,记得要实现接口
        mactivityBinding.onClickListener = this
    }
    //当点击按钮的时候就会自动调用到这里了
    override fun onClick(v: View?) {
        when(v){
            mactivityBinding.button7 -> {
                mactivityBinding.textView.text = "这个是点击button7产生的"
            }
            mactivityBinding.button8 ->{
                mactivityBinding.textView2.text = "这个是点击button8产生的"
            }
        }
    }
}

直接看上面注释就知道用法了

上面的注释看懂就可以了,Activity中基本的DataBinding你就算是掌握了,关于点击事件的还有很多种写法,可能需要重新写一篇详细的说一下其它的细节吧。

六、在Fragment中使用

    lateinit var fragmentTestBinding:FragmentTestBindingBinding
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        fragmentTestBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_test_binding,container,false)
        return fragmentTestBinding.root
    }

Activity对比Fragment这里就是DataBindingUtil.inflate()传参不同,然后返回fragmentTestBinding.root。其它的操作都是一样的

七、单向绑定数据(可观察的数据对象)

最开始使用的时候我很不理解为什么还有个单向绑定。因为我看到基本类型的数据,在xmldata定义 然后再Activity中更改,页面会跟着一起刷新,这不就是相当于单向绑定了吗???直到后面我在<data>节点中import入自定义的数据类bean,然后再在Activity中更改bean中的成员变量时,页面并没有改变。这时候我才懂了这个存在的原因。(ps:这个只是个人刚接触的时候想法,新手有可能也有会这样想法,我这里先说一下)

官网定义:任何普通的旧对象都可以用于数据绑定,但是修改对象不会自动导致UI更新。数据绑定可用于使您的数据对象在数据更改时通知其他对象(称为侦听器)。有三种不同类型的可观察类: 对象,字段和集合

实现的基本方法有以下几种

1. 直接定义可监听变量
  • ObservableBoolean
  • ObservableByte
  • ObservableChar
  • ObservableShort
  • ObservableInt
  • ObservableLong
  • ObservableFloat
  • ObservableDouble
  • ObservableParcelable
//定义的实体bean时候
class Person {
    val Name = ObservableField<String>()
    val Age = ObservableInt()
}
//(kotlin 语法 )调用实体bean的时候
user.firstName = "Google"
val age = person.Age 
--------------------------------------------------------
//(java 语法 )调用实体bean的时候
person.firstName.set("Man");
int age = person.Age.get();

使用的时候记得使用get(),set()方法。(ps:kotlin语法看来起来是直接调用的,但实际上里面也是实现了get()set()方法)

//可监听变量数组类型

  • ObservableArrayMap<>()
  • ObservableArrayList<>()

----------定义xml文件-----------------------
<data>
        <import type="androidx.databinding.ObservableArrayList"/>
        <variable
            name="list"
            type="ObservableArrayList&lt;String>" />
</data>
…

<TextView
    android:text='@{list[0],defualt = "hello"}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
}
------------使用的时候----------------------------

ObservableArrayList<String>().apply {
    add("man")
}

用法跟基本类型一致,但是有两点要注意

  1. 记得在<data>import入对应的数组的包名所在路径
  2. 引用这些集合类型的时候,<要改为\&lt; (ps:Android stuido会提醒你这样子操作的 )
2. ObservableField<T>,这个跟上面那些用法一样,泛型传入指定类型。
3. 继承 BaseObservable()
-------------------------------kotlin写法-----------------------------
class User :BaseObservable(){
    @get:Bindable
    var name:String = ""
    set(value) {
        field = value
        notifyPropertyChanged(BR.name)
    }
    @get:Bindable
    var age:Int = 0
    set(value) {
        field = value
        notifyPropertyChanged(BR.age)
    }
}
--------------------------------java写法---------------------------------
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);
    }
}

实体类继承BaseObservable()

  1. 在对应的字段get方法钱前面加上注解@Bindable ,kotlin@get:Bindable
  2. set方法中修改数据后调用notifyPropertyChanged(BR.name)通知更改数据
  3. 如果数据类的基类不能被改变,可观察到的界面可以实现使用PropertyChangeRegistry对象注册和通知监听器。(补充)

这里有个坑!!! BR总是报错 找不到nameage字段,后来百度需要在对应module项目下的build.gradle上面加上插件

apply plugin: 'kotlin-kapt'

八、双向绑定数据

在xml文件中 单向绑定我们是用@{变量名},双向绑定则是@={变量名}
最常见的需求就是我们的页面输入了。输入的数据要在代码中可以让我们接收到。

 <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@{title,default =标题}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="@={title}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3" />

editText数据数据的时候android:text="@={title}"双向绑定数据,TextView中绑定的 android:text="@{title,default =标题}"也会跟着一起变化。

九、在include中使用

在include中:
先定义include_text.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="testview.zhen.com.myapplication.bean.Person"/>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{`嵌套布局 用户名:`+peronInfo.name}"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{`嵌套布局 年龄:`+peronInfo.age}"
        />
</LinearLayout>
</layout>

在布局中引入

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <include
        layout="@layout/inclue_text"
        app:peronInfo = "@{peronInfo}"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

include标签中app:peronInfo将数据传入,这里的peronInfo是在include布局中我们自己定义的数据。

十、在viewStub中使用

因为viewStub是属于懒加载的布局,如果不调用inflate,就不会加载。属于安卓上面也是页面优化有一个范畴。

先定义一个viewStub布局页面

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="testview.zhen.com.myapplication.bean.Person"/>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<LinearLayout
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{peronInfo.name + peronInfo.age}"
        />
</LinearLayout>
</layout>

在布局中引入

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <ViewStub
            android:id="@+id/view_stub"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:layout="@layout/viewstub_layout"
            app:peronInfo = "@{peronInfo}"
            />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

以上的操作跟include的用法一致,不同点在于Activity中的绑定
Activity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //绑定布局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        o.add(0,"第一数据")
        mactivityBinding.peronInfo = Person("man",18,o)
        mactivityBinding.viewStub.viewStub!!.inflate()
    }

关键代码mactivityBinding.viewStub.viewStub!!.inflate()这里就是加载viewStub的地方。

十一、在Adapter中的使用

简化统一可以使用的ViewHolder

public class BaseViewHolder extends RecyclerView.ViewHolder {
    public BaseDataBinding mbinding;

    public BaseViewHolder(BaseDataBinding binding) {
        super(binding.getRoot());
        mbinding = binding;
    }
}

你可以使用上面的ViewHolder就可以不用自己总是去写ViewHolder了。

val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

AdapteronCreateViewHolder中可以使用上面的方法绑定布局

关于AdapterDataBinding使用的网上有很多文章大家可以自行查阅

这篇文章主要是记录一下自己学习的过程,很多细节化跟封装的话以后再出一篇文章另外记录,有问题的也请同学们在评论区留言,希望看的同学们觉得有用的话给个赞~ 谢谢

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

推荐阅读更多精彩内容