Android DataBinding初探
官方的文档有一点晦涩难读,如果直接看案例也很头疼
今天算是对DataBinding有点初步的了解了,就用几个案例记录一下DataBinding的使用,顺便也是一个分享
先放上谷歌官方文档的DataBinding这一部分的链接,要是想深入了解还是推荐看文档
什么是Databinding
数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。
布局通常是使用调用界面框架方法的代码在 Activity 中定义的。
简单的说以前对数据和控件的操作非常繁琐,先要用findViewById()
来获取控件,每次数值改变之后,还需要对控件的内容重新进行设置
而这个DataBinding可以把对象和控件绑定,每次对象的值改变了,相对的控件也会刷新,这个就和以前MFC
上面的那个绑定变量差不多
DataBinding会根据布局文件自动生成相应的Binding类,在代码中是找不到这个类的,这个类应该是编译阶段生成的,但是我们可以直接去使用
这种布局文件和传统的布局文件有一点点差别,下面放一个案例
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!-- 这里放一些变量的定义,后面会解释 -->
</data>
<!-- 把原先的布局文件的内容放到这里(layout的里面) -->
</layout>
使用
配置项目
只需要在module
里面配置
android {
···
原来已经有的一些配置
···
dataBinding {
enabled = true
}
}
一般来说使用DataBinding的话只要你的SDK和Gradle不要太旧的话,基本上都不用管版本什么的,如果遇到了问题就百度一下吧
在Activity里面使用
先写一个很简单的java Bean
public class UserBean {
private String name; //姓名
private int age; //年龄
public UserBean(String name, int age) {
this.name = name;
this.age = age;
}
···
此处省略getter和setter方法
···
}
我们修改activity的xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!-- 使用全类名 -->
<!--
<variable name = "user" type = "com.kehao.databinding1.bean.UserBean"
/>
-->
<!-- import -->
<import type="com.kehao.databinding1.bean.UserBean"/>
<variable name="user" type="UserBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name }" />
<!--注意:这里age是int类型,必须转化为String,否则会运行时异常-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}" />
</LinearLayout>
</layout>
先对这段xml代码中data标签里的内容进行解释
<variable name = "user" type = "com.kehao.databinding1.bean.UserBean" />
这个就是相当于在布局文件中声明一个变量 等效于java中的 UserBean user
,不过并没有new
,这个变量具体的引用是靠java代码给他的,后面这个type
这么长,是全类名,如果不想写全类名就用import标签
这个和java里面的import也差不多
就像这样
<import type="com.kehao.databinding1.bean.UserBean"/>
<variable name="user" type="UserBean"
然后定义里变量之后,在后面的控件中就可以使用啦
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name }" />
还有
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}" />
反正引用的话就是@{这个里面使用java代码}
这样子这个类就和布局文件产生了联系,具体是哪一个变量和控件绑定就需要在Activity里面确定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
UserBean bean = new UserBean("李明",20);
binding.setUser(bean);
//setContentView(R.layout.activity_main);
bean.setAge(200);//改变变量,界面上的控件上的值也随之相应改变
}
}
默认的activity
的onCreate
函数内容是这样的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
因为布局文件不是原先的哪种模式了,所以就要换一个setContentView
函数
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
这个函数顺便就返回了对应布局文件的Binding对象
通过这个对象我们就可以给xml文件中声明却未初始化的变量赋值了
UserBean bean = new UserBean("李明",20);
binding.setUser(bean);//这个函数也是自动生成的,只要在xml中声明了的variable标签,都会生成一个相应的`setXxx`函数,用于赋值
运行起来
效果图
在Fragment里面使用
随便创建一个Activity,然后在里面放一个Fragment控件,在主界面放一个按钮,跳转到这个activity
编写fragment的布局文件代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name = "user"
type = "com.kehao.databinding1.bean.UserBean"
/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F44336"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a red fragment" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name }" />
<!--注意:这里age是int类型,必须转化为String,否则会运行时异常-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}" />
</LinearLayout>
</layout>
然后在Fragment中编写代码
public class BlankFragment extends Fragment {
private FragmentBlankBinding binding;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentBlankBinding.inflate(inflater);
UserBean user = new UserBean("张三",11);
binding.setUser(user);
return binding.getRoot();
}
}
关键是binding = FragmentBlankBinding.inflate(inflater);
获取binding对象
在RecyclerView中使用
先创建一个Activity,里面放上RecyclerView,在主界面添加一个按钮跳转之
Activity的代码
public class RecyclerActivity extends AppCompatActivity {
private ActivityRecyclerBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_recycler);
RecyclerView recyclerView = binding.recyclerview;
MyRecyclerViewAdapter adapter = new MyRecyclerViewAdapter();
adapter.list = new ArrayList<UserBean>();
initList(adapter.list);
adapter.fun = ()-> Toast.makeText(this, "you touch it", Toast.LENGTH_SHORT).show();
recyclerView.setAdapter(adapter);
LinearLayoutManager manager = new LinearLayoutManager(RecyclerActivity.this);
manager.setOrientation(RecyclerView.VERTICAL);
recyclerView.setLayoutManager(manager);
}
private void initList(List<UserBean> list){
for (int i = 0 ;i<10 ;i++){
list.add(new UserBean("李"+i,i));
}
}
}
其中RecyclerView recyclerView = binding.recyclerview;
是因为只要在布局文件中配置了id,就可以直接获取该控件对象,其他的代码都是很常见的RecyclerView的使用代码
recyclerview里面的每一项的布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.kehao.databinding1.bean.UserBean"/>
<import type="com.kehao.databinding1.FunInterface"/>
<variable name="user" type="UserBean" />
<variable name="fun" type="FunInterface" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:onClick="@{()->fun.toDo()}"
>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#F44336"
android:text="@{user.name}" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#4CAF50"
android:text="@{String.valueOf(user.age)}" />
</LinearLayout>
</layout>
其中android:onClick="@{()->fun.toDo()}
,这里的fun对象是一个接口,可以通过Binding对象的set方法来传入一个实现类,通过这种配置可以很方便的设置点击事件,而不需要复杂的setOnClickListener
的办法
不过,官方文档中是可以使用方法引用的,但我这里不知道为什么使用方法引用的时候会报错找不到binding类,我之前填写的是@{fun::toDo},不知道是不是方法引用是不能够多态的,而这里的方法没有实现,所以我只能使用lambda表达式了,希望知道的大佬能够告诉我
附上官方文档里的案例截图
然后创建adapter对象
package com.kehao.databinding1.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.kehao.databinding1.FunInterface;
import com.kehao.databinding1.R;
import com.kehao.databinding1.bean.UserBean;
import com.kehao.databinding1.databinding.UseritemBinding;
import java.util.List;
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder>{
public List<UserBean> list;
public FunInterface fun;
@NonNull
@Override
public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.useritem,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder (@NonNull MyRecyclerViewAdapter.ViewHolder holder, int position) {
UserBean user = list.get(position);
holder.binding.setUser(user);
holder.binding.setFun(fun);
}
@Override
public int getItemCount() {
return list.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
UseritemBinding binding;
public ViewHolder(@NonNull View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
}
}
ViewHolder承载的就是每一项的视图,而每一个Binding对象是和布局文件相关联的,所以我们就要在ViewHolder的构造函数中,获取binding对象,并将其作为成员变量方便以后调用
static class ViewHolder extends RecyclerView.ViewHolder{
UseritemBinding binding;
public ViewHolder(@NonNull View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
}
而视图和变量绑定就在
@Override
public void onBindViewHolder (@NonNull MyRecyclerViewAdapter.ViewHolder holder, int position) {
UserBean user = list.get(position);
holder.binding.setUser(user);
holder.binding.setFun(fun);
}
所以最后的效果就是
效果图
点击之后
还有一些更加高级的使用办法,可以参见官方文档
案例代码我放到github上了:https://github.com/chenkehao1998/databinding