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()
}
[本章完...]