JetPack<第一篇>:DataBinding

2022-08-08  本文已影响0人  NoBugException

【1】导入依赖

implementation 'androidx.databinding:databinding-runtime:4.2.2'

【2】 在app模块下的 build.gradle 文件添加内容

android {
    ...
    dataBinding {
        enabled true
    }
}

或者 

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

另外,如果在 android 闭包下没有指定 java 1.8 版本的话,需要添加:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
    jvmTarget = '1.8'
}

【3】 布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

    </LinearLayout>
</layout>

layout:用作布局的根节点,只能包裹一个View标签,且不能包裹merge标签
data:Data Binding的数据,只能存在一个data标签

<data class="ActivityMainBinding">

</data>

data 的 class 用来自定义代码中Activity中的ViewBinding使用的<ActivityMainBinding>的名称。


<data> 标签中可以有 <import> 和 <variable> 两大标签:

<data class="ActivityMainBinding">
    <import
        type="com.yunchong.jetpack.model.LoginModel" />
    <variable
        name="loginModel"
        type="LoginModel" />
</data>

import 用于导入一个类,variable  用于定义一个变量,变量 loginModel 需要和 view 绑定:

private lateinit var binding : ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    val loginModel = LoginModel(this, "zhangsan", "123456");
    binding.loginModel = loginModel
}

当然,setContentView 也有所变化。

import 可以配置别名:

<data class="ActivityMainBinding">
    <import
        alias="LoginModelAlias"
        type="com.yunchong.jetpack.model.LoginModel" />
    <variable
        name="loginModel"
        type="LoginModelAlias" />
</data>

variable 定义的对象在xml中的使用:

<data class="ActivityMainBinding">
    <variable
        name="loginModel"
        type="com.yunchong.jetpack.model.LoginModel" />
</data>

private lateinit var binding : ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    val loginModel = LoginModel(this, "zhangsan", "123456");
    binding.loginModel = loginModel
}

<?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 class="ActivityMainBinding">
        <variable
            name="loginModel"
            type="com.yunchong.jetpack.model.LoginModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <TextView
            android:id="@+id/tv_activity_main_account"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="账号:"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:textSize="26sp"/>
        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/et_activity_main_account"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textSize="26sp"
            android:hint="输入账号"
            android:onTextChanged="@{() -> loginModel.accountNameChanged()}"
            android:text="@={loginModel.accountNameField.get()}"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBaseline_toBaselineOf="@id/tv_activity_main_account"
            app:layout_constraintLeft_toRightOf="@id/tv_activity_main_account"/>
        <TextView
            android:id="@+id/tv_activity_main_pwd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="密码:"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_activity_main_account"
            android:textSize="26sp"/>
        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/et_activity_main_pwd"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textSize="26sp"
            android:hint="输入密码"
            android:onTextChanged="@{() -> loginModel.passwordChanged()}"
            android:text="@={loginModel.passwordField.get()}"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBaseline_toBaselineOf="@id/tv_activity_main_pwd"
            app:layout_constraintLeft_toRightOf="@id/tv_activity_main_pwd"/>

            <androidx.appcompat.widget.AppCompatButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingVertical="10dp"
                android:paddingHorizontal="20dp"
                android:textSize="20sp"
                android:enabled="@{(loginModel.accountNameField.get().isEmpty() || loginModel.passwordField.get().isEmpty()) ? false : true}"
                android:onClick="@{() -> loginModel.login()}"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:text="登录"/>

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

在xml布局中使用 `android:text="@{}"` 将数据和view绑定,但仅仅是单向绑定,当数据变化的时候,view 也会跟着改变。
当我们想改变布局中的数据时,只需要变化 model 中的值即可,而不需要主动修改布局中的数据显示;
但是,当布局中的数据发生变化时,model 中数据是无法发生改变的。

为了解决`单向绑定`的弊端,提出了 `双向绑定`。

 `@{}` 是单向绑定, `@={}` 是双向绑定,单向绑定和双向绑定的区别就是有无 `=` 的区别。
双向绑定:当 model 发生变化时,布局中的数据也跟着改变;
          当布局中的数据发生改变时, model 也发生改变。


另外,注意布局中:
android:onTextChanged
android:enabled
android:onClick
的使用。
下面,贴出model代码:

/**
 * 登录Model
 */
class LoginModel(context: Context, accountName : String, password : String) {

    val context: Context = context;
    val accountNameField = ObservableField<String>(accountName)
    val passwordField = ObservableField<String>(password)

    /**
     * 输入账号变化时执行
     */
    fun accountNameChanged() {
        Log.d("yunchong", accountNameField.get() as String)
    }

    /**
     * 输入密码时执行
     */
    fun passwordChanged() {
        Log.d("yunchong", passwordField.get() as String)
    }

    /**
     * 登录
     */
    fun login() {
        if ("zhangsan" == accountNameField.get() && "123456" == passwordField.get()) {
            Toast.makeText(context, "登录成功...", Toast.LENGTH_SHORT).show()
            startActivity<DataActivity>(context) {
                putExtra("acountName", accountNameField.get())
                putExtra("password", passwordField.get())
            }
        } else {
            Toast.makeText(context, "登录失败...", Toast.LENGTH_SHORT).show()
        }
    }
}

Activity 的跳转是被封装好的,直接拿来用即可:

/**
 * 启动 Activity
 */
inline fun <reified T> startActivity(context: Context, block: Intent.() -> Unit) {
    val intent = Intent(context, T::class.java)
    intent.block()
    context.startActivity(intent)
}

【4】在Activity中使用DataBinding

mBinding = DataBindingUtil.setContentView(this, R.layout.xxxxx)

或者

mBinding = XXXXXBinding.inflate(layoutInflater)
setContentView(mBinding.root)

【5】在Fragment中使用DataBinding

如果在Fragment中使用,布局是一样的,Fragment代码如下:

class LoginFragment : Fragment() {

    private var binding : FragmentLoginBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // binding = FragmentLoginBinding.inflate(inflater)
        // 或
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false);
        return binding?.root
    }

}

【6】在Adapter中使用

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 
    RecyclerView.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
        val bidning:RecycleItemProductBinding =  DataBindingUtil.bind(view) 
}

【7】include 标签的使用

include 标签不带 merge 标签,需要给 include 标签添加 id, 直接使用 id 即可。

<include
    android:id="@+id/includeData"
    layout="@layout/layout_include_data_item"/>

binding.includeData.includeTvTitle.setText("")

include 标签带 merge 标签,注意这里和 ViewBinding 用法不一样,给 include 标签添加 id,在 DataBinding 中可以直接使用,在 ViewBinding 中则不行。

<include
    android:id="@+id/includeDataMerge"
    layout="@layout/layout_merge_data_item"/>

binding.includeDataMerge.mergeTvTitle.setText("通")

【8】ViewStub 标签的使用

给 ViewStub 标签添加 id, 在 DataBinding 中可以直接使用 id 即可。

<ViewStub
    android:id="@+id/stub"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout="@layout/view_stub" />

binding.stub.setOnInflateListener { stub, inflated ->
    // DataBinding
    val dataViewStub: ViewStubDataBinding = DataBindingUtil.bind(inflated)!!
    dataViewStub.tvTitle.setText("使用 ViewStub 加载 DataBinding 布局")
}
            
if (!binding.stub.isInflated) {
    binding.stub.viewStub!!.inflate()
} 

[本章完...]

上一篇下一篇

猜你喜欢

热点阅读