jetPack组件必学#DataBinding

43 阅读6分钟

jetPack组件必学#Lifecycle

jetPack组件必学#LiveData&ViewMode

jetPack组件必学#DataBinding

jetPack组件必学#Dagger2&Hilt

jetPack组件必学#Navigation

jetPack组件必学#Room

一.基本使用

1.让项目支持databining

plugins {
    id 'kotlin-kapt'
    }
​
android {
    buildFeatures {
        dataBinding true
    }
}

2.布局和绑定表达式

选中xml文件的根目录,按住alt+enter 选择covert to data binding layout

 <?xml version="1.0" encoding="utf-8"?>
<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">
 <data>
        <variable
            name="dataModel"
            type="com.seven.databinding.DataModel" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
​
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{dataModel.str}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.对象与xml的单向绑定方式

3.1 变量单次修改时修改控件的单向绑定

​
    class DataModel {
        var str: String? = null
    }
    
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val contentView =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val dataModel = DataModel()
        dataModel.str = "abc"
        contentView.dataModel=dataModel
        //通过上面的数据绑定textView中显示的内容就为dataModel的str这个属性值,但是只有一次,后面就不会修改了
    }
}

3.2 变量修改时修改控件的单向绑定

使用ObservableField或继承自BaseObservable方式,这样后续数据变化,控件的显示就会跟踪变化

/**
 * 单向绑定 数据层单向给view赋值,当数据层发生修改时,view产生修改
 * 1.
 * 2.ObservableField方式
 * 3.extends Observable方式
 *
 */class DataModel1 {
    var str:String? ="default"
}
class DataModel2 {
    var str: ObservableField<String> = ObservableField("default")
}
class DataModel3 : BaseObservable() {
    @Bindable
    var str2: String?=null
        set(value) {
            field = value
            println("value:$value")
            notifyPropertyChanged(BR.str2)
        }
}
​
class MainActivity : AppCompatActivity() {
    lateinit var binding:ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val data2 = DataModel2()
        binding.datamodel2 = data2
        data2.str.addOnPropertyChangedCallback(object : OnPropertyChangedCallback(){
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
                //因为是单向绑定,所以当延时2秒去修改view是,这个里面就收不到
                println("delay after propertyId$propertyId,${binding.text.text},str2:${data2.str.get()}")
            }
        })
        thread {
            Thread.sleep(1000)
            runOnUiThread {
                println("delay single bingding field change ui")
                data2.str.set("cde")
​
            }
        }
        thread {
            Thread.sleep(2000)
            println("delay run---------> ")
            runOnUiThread {
                binding.text.text = "fgh"
            }
        }
    }
    /**
     * 记得销毁,防止内存泄漏
     */
    override fun onDestroy() {
        super.onDestroy()
        binding.unbind()
    }
}
​
​
<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
​
        <variable
            name="datamodel2"
            type="com.seven.databinding.DataModel2" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
​
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={datamodel2.str}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4.双向绑定

修改上面的xml文件即可

<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
​
        <variable
            name="datamodel2"
            type="com.seven.databinding.DataModel2" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
​
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={datamodel2.str}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

二.事件绑定

1.写一个Event处理器

package com.seven.databinding
​
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.databinding.*
import androidx.databinding.Observable.OnPropertyChangedCallback
import com.seven.databinding.databinding.ActivityMainBinding
import kotlin.concurrent.thread
​
/**
 * 一.单向绑定 数据层单向给view赋值,当数据层发生修改时,view产生修改
 * 1.常规变量
 * 2.ObservableField方式
 * 3.extends Observable方式
 * 二.双向绑定
 * 修改xml文件即可
 * 三.事件的绑定
 *
 *
 */class EventHandler(private val context: Context) {
​
    fun onBtnClick(view: View) {
        Toast.makeText(context, "view on click", Toast.LENGTH_SHORT).show()
    }
}
​
class DataModel1 {
    var str:String? ="default"
}
class DataModel2 {
    var str: ObservableField<String> = ObservableField("default")
}
class DataModel3 : BaseObservable() {
    @Bindable
    var str2: String?=null
        set(value) {
            field = value
            println("value:$value")
            notifyPropertyChanged(BR.str2)
        }
}
​
class MainActivity : AppCompatActivity() {
    lateinit var binding:ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val data2 = DataModel2()
        binding.datamodel2 = data2
        data2.str.addOnPropertyChangedCallback(object : OnPropertyChangedCallback(){
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
                //因为是单向绑定,所以当延时2秒去修改view是,这个里面就收不到
                println("delay after propertyId$propertyId,${binding.text.text},str2:${data2.str.get()}")
            }
        })
        thread {
            Thread.sleep(1000)
            runOnUiThread {
                println("delay single bingding field change ui")
                data2.str.set("cde")
​
            }
        }
        thread {
            Thread.sleep(2000)
            println("delay run---------> ")
            runOnUiThread {
                binding.text.text = "fgh"
            }
        }
​
        //设置eventHandler
        val eventHandler = EventHandler(this)
        binding.eventHandler = eventHandler
    }
    /**
     * 记得销毁,防止内存泄漏
     */
    override fun onDestroy() {
        super.onDestroy()
        binding.unbind()
    }
}

2.在xml中配置点击事件

 <?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
​
        <variable
            name="datamodel2"
            type="com.seven.databinding.DataModel2" />
        <variable
            name="eventHandler"
            type="com.seven.databinding.EventHandler" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
​
        <TextView
            android:clickable="true"
            android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={datamodel2.str}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.带参数的Event处理器

修改xml文件配置

<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
​
        <variable
            name="datamodel2"
            type="com.seven.databinding.DataModel2" />
        <variable
            name="eventHandler"
            type="com.seven.databinding.EventHandler" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
​
        <TextView
            android:clickable="true"
            android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={datamodel2.str,default=`ccc`}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
        <TextView
            android:onClick="@{(view)->eventHandler.onBtnClick(datamodel2.str.toString(),view)}"
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={datamodel2.str,default=`eeee`}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/text" />
​
​
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4.添加带参数的点击事件

class EventHandler(private val context: Context) {
​
    fun onBtnClick(view: View) {
        Toast.makeText(context, "view on click", Toast.LENGTH_SHORT).show()
    }
​
    /**
     * 带参数的点击事件
     */
    fun onBtnClick(str:String,view: View) {
        Toast.makeText(context, "view on click $str", Toast.LENGTH_SHORT).show()
    }
}
​

三.常用绑定适配、binding的传递、一些表达式的写法

1.RecycleView搭配databinding

编写xml布局:1.需要新增一个命名空间;2.配置自定的适配器方法sob:setImage="@{item.url}"

  <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:sob="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
​
    <data>
        <variable
            name="item"
            type="com.seven.databinding.DBBean" />
​
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
​
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.3" />
​
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.2" />
​
        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.name}"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toStartOf="@+id/guideline2"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:src="@{item.url}"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.201"
            app:layout_constraintStart_toStartOf="@+id/guideline2"
            app:layout_constraintTop_toTopOf="parent"
            sob:setImage="@{item.url}" />
​
        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent"
            sob:srcCompat="@drawable/ic_launcher_background" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2. 设置自定义holder

1.将databinding传入ViewHolder中去;2.自定义适配器加载图片

   data class DBBean(val name: String, val url: String)
class Adapter: RecyclerView.Adapter<Adapter.VH>() {
    val data: MutableList<DBBean> by lazy {
        mutableListOf()
    }
​
    //伴生对象做加载图像是适配器
    companion object {
        @BindingAdapter("setImage")
        @JvmStatic
        fun loadImage(view: ImageView?, url: String?) {
            println("loadImage->>>>>>>$url")
            // view?.setImageResource(R.drawable.ic_launcher_foreground)
​
        }
​
        @BindingAdapter("android:src")
        @JvmStatic
        fun loadImage2(view: ImageView?, url: String?) {
            println("loadImage2->>>>>>$url")
            // view?.setImageResource(R.drawable.ic_launcher_foreground)
        }
​
    }
    inner  class VH(item: View, binding: ItemRvBinding): RecyclerView.ViewHolder(item)
    /**
     * 作为局部变量设置进去,不能作为全局变量哟,全局变量会一直是同一个
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        val binding = DataBindingUtil.inflate<ItemRvBinding>(
            LayoutInflater.from(parent.context), R.layout.item_rv, parent, false
        )
        return VH(binding.root, binding)
    }
    override fun onBindViewHolder(holder: VH, position: Int) {
​
    }
​
    override fun getItemCount(): Int =data.size
}

3.databinding的传递

 <!--一级界面-->
<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
        <variable
            name="eventHandler"
            type="com.seven.databinding.MainActivity3.EventHandler" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
​
        <TextView
            android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <include
            app:event="@{eventHandler}"
            android:id="@+id/sub"
            layout="@layout/sub_activity_main3"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
 <!--二级界面-->
<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
        <variable
            name="event"
            type="com.seven.databinding.MainActivity3.EventHandler" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
​
        <TextView
            android:onClick="@{(view)->event.onBtnClick(view)}"
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4.一些表达式的写法

官网

       <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
​
            <TextView
                android:id="@+id/textView4"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:background="@color/cardview_shadow_start_color"
                android:text="@{db.str}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
​
            <Button
                android:id="@+id/button"
                android:src="@{@drawable/ic_launcher_background}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{@string/format_name('1','2','3')}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView4" />

四.binding结合liveData使用

1.结合liveData使用有两个好处

1.liveData不用在添加观察者;

2.数据类不需要继承自BaseObServable或者使用ObservableField

2.怎么使用?

1.新建一个ViewModel

2.在viewModel中添加liveData

3.在xml中引用这个vm

4.在Activity中给binding设置这个vm,注意需要设置lifecycleOwner!!!

class DataModel3Vm :ViewModel(){
​
    val liveData= MutableLiveData<DataModel3>()
    
}
class DataModel3 {
    var str2:String?=null
}
​
        /**
         * binding.dataModelVm=DataModel3Vm()这样就不会
         * 为DataModel3Vm中的liveData去添加观察者,但是
         * 需要给其设置lifecycleOwner,通过binding.lifecycleOwner
         * 传递过去的
         *
         */
        val dataModel3Vm=DataModel3Vm()
        binding.lifecycleOwner=this
        binding.dataModelVm=dataModel3Vm
        thread {
           var dataModel3= DataModel3()
            dataModel3.str2="abc"
            dataModel3Vm.liveData.postValue(dataModel3)
            Thread.sleep(2000)
            dataModel3.str2="cde"
            dataModel3Vm.liveData.postValue(dataModel3)
        }
<?xml version="1.0" encoding="utf-8"?>
<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">
​
    <data>
​
        <variable
            name="dataModel"
            type="com.seven.databinding.DataModel" />
​
        <variable
            name="dataModel2"
            type="com.seven.databinding.DataModel2" />
​
        <variable
            name="dataModelVm"
            type="com.seven.databinding.DataModel3Vm" />
​
        <variable
            name="eventHandler"
            type="com.seven.databinding.EventHandler" />
    </data>
​
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:context=".MainActivity">
​
​
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:onClick="@{eventHandler.onBtnClick}"
            android:text="@={dataModel.str, default=`name`}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
​
        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:background="@color/cardview_dark_background"
            android:onClick="@{(view)->eventHandler.onBtnClick2(dataModel2.str2.toString(),view)}"
            android:onLongClick="@{(view)->eventHandler.onBtnClickLong(dataModel2.str2.toString(),view)}"
            android:text="@={dataModel2.str2, default=`name`}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />
​
        <TextView
            android:id="@+id/textView3"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:background="@color/cardview_dark_background"
            android:text="@={dataModelVm.liveData.str2}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2" />
​
        <include
            layout="@layout/sub_main"
            app:db="@{dataModel}"
            android:id="@+id/sub"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView3" />
​
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/sub" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>