dataBinding,数据绑定。
优点
- 自动检测null值,避免空指针异常
- 防止内存泄漏
- 可双向绑定数据
引用
在app的build.gradle文件的Android{}里面添加如下代码:
dataBinding {
enabled = true
}
相关语法
layout
xml文件最外层的标签。
data
xml文件数据位置的标签。
import
xml文件中引用文件的标签。内有alias(别名)。
variable
xml文件中的变量。内有name(命名)和type(类型)。
@{}
在大括号内写变量,对象变量或者表达式。
@={}
双向数据绑定。
/*变量*/
android:text="@{String.valueOf(position)}"
/*对象变量*/
android:text="@{user.name}"
/*表达式*/
android:text="@{user.name !=null ? user.name : "用户"}"
Null合并运算符??
左边内容不为空,则使用左边内容,否则使用右边内容。
android:text="@{user.displayName ?? user.lastName}"
/*等价于*/
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
引用视图
官方例子
<EditText
android:id="@+id/example_text"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<TextView
android:id="@+id/example_output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{exampleText.text}"/>
二级页面使用对象
如果二级页面的variable的name是secondaryPageBean,那么一级页面include的布局可以直接引用传递对象,使用app开头或者bind开头,代码如下:
二级页面
<?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">
<data>
<variable
name="secondaryPageBean"
type="com.elaine.testdatabinding.secondarypage.SecondaryPageBean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{secondaryPageBean.name}"
android:textSize="24sp"
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>
一级页面
<?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="bean"
type="com.elaine.testdatabinding.secondarypage.SecondaryPageBean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".secondarypage.SecondaryPageActivity">
<include
layout="@layout/layout_secondary_page"
app:secondaryPageBean="@{bean}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
自定义属性BindingAdapter
使用注解@BindingAdapter来定义方法,即可在xml文件中使用。
注意:在要引入自定义属性的时候,需要先把xml文件的最外层改为layout。MyBindingAdapter.java
package com.elaine.testdatabinding.testbindingadapter;
import android.text.TextUtils;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.databinding.BindingAdapter;
/**
* bindingAdapter 文件
* author: elaine
* date: 2021/5/13
*/
public class MyBindingAdapter {
/**
* 单个参数
*
* @param imageView 图片控件
* @param res 资源
*/
@BindingAdapter("myImageRes")
public static void setMyImageViewRes(ImageView imageView, int res) {
imageView.setImageResource(res);
}
/**
* 多个参数
* requireAll = false 表示可以传一个参数
*
* @param textView 文字控件
* @param content 内容
* @param defaultContent 默认内容
*/
@BindingAdapter(value = {"content", "defaultContent"}, requireAll = false)
public static void setMyTextViewContent(TextView textView, String content, String defaultContent) {
if (!TextUtils.isEmpty(content)) {
textView.setText(content);
} else {
textView.setText(defaultContent);
}
}
}
TestBindingAdapterActivity.java
package com.elaine.testdatabinding.testbindingadapter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.elaine.testdatabinding.R;
import com.elaine.testdatabinding.databinding.ActivityTestBindingAdapterBinding;
public class TestBindingAdapterActivity extends AppCompatActivity {
private ActivityTestBindingAdapterBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_test_binding_adapter);
mBinding.setMyImageRes(R.mipmap.ic_launcher_round);
mBinding.setContent("正式数据");
mBinding.setDefaultContent("默认数据");
}
}
activity_test_binding_adapter.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="myImageRes"
type="int" />
<variable
name="content"
type="String" />
<variable
name="defaultContent"
type="String" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".testbindingadapter.TestBindingAdapterActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="64dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:myImageRes="@{myImageRes}" />
<TextView
android:id="@+id/tv_content"
content="@{content}"
defaultContent="@{defaultContent}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
结合liveData,viewModel使用
TestWithLiveDataActivity.java
package com.elaine.testdatabinding.testwithlivedata;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;
import com.elaine.testdatabinding.R;
import com.elaine.testdatabinding.databinding.ActivityTestWithLiveDataBinding;
public class TestWithLiveDataActivity extends AppCompatActivity {
private ActivityTestWithLiveDataBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_test_with_live_data);
TestWithLiveDataViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(TestWithLiveDataViewModel.class);
mBinding.setViewModel(viewModel);
mBinding.setLifecycleOwner(this);
}
}
TestWithLiveDataViewModel.java
package com.elaine.testdatabinding.testwithlivedata;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
/**
* author: elaine
* date: 2021/5/13
*/
public class TestWithLiveDataViewModel extends ViewModel {
private MutableLiveData<String> content;
public MutableLiveData<String> getContent() {
if (content == null) {
content = new MutableLiveData<>();
content.setValue("默认数据");
}
return content;
}
public void setContent(String con) {
content.setValue(con);
}
}
activity_test_with_live_data.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="viewModel"
type="com.elaine.testdatabinding.testwithlivedata.TestWithLiveDataViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".testwithlivedata.TestWithLiveDataActivity">
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:text="@{viewModel.content}"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.524"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:onClick="@{() -> viewModel.setContent(`现在是正式数据`)}"
android:text="更改数据"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.537"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
小tips
- 设计阶段可以用tools属性显示默认值
- xml文件中转义字符<用<表示
顺带提一下ViewBinding
viewBinding,视图绑定,用来替代findViewById。
引用
在app的build.gradle文件的Android{}里面添加如下代码:
viewBinding {
enabled = true
}
对比findViewById
优点
- null安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。
- 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
- 方便使用:不需要特地写控件变量和findViewById()方法。
对比ViewBinding
优点
- 编译更快:不需要处理注释。
- 易于使用。
缺点
- 不支持双向数据绑定。
- 不支持布局变量或布局表达式。
视图绑定方法
MainActicity.java
package com.elaine.testdatabinding;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.elaine.testdatabinding.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding activityMainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(activityMainBinding.getRoot());
}
}
MainFragment.java
package com.elaine.testdatabinding;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import com.elaine.testdatabinding.databinding.FragmentMainBinding;
public class MainFragment extends Fragment {
private FragmentMainBinding fragmentMainBinding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fragmentMainBinding = FragmentMainBinding.inflate(inflater, container, false);
return fragmentMainBinding.getRoot();
}
}
上面的开关随下面的开关变换,同理下面的开关。
项目github地址
更多详情参考官方文档
developer.android.google.cn/topic/libra…