dataBinding是google推出来的一个mvvm的框架,出来有一段时间了,由于之前的项目都是用mvc或者mvp的模式开发,没有使用mvvm模式进行开发,这次公司项目用的是jetpack框架,涉及到ViewModel、dataBinding等,所以就利用些空闲时间了解和学习下dataBinding的一些使用。
准备工作:
要使用dataBinding的话,首先要在app model 下build.gradle文件的android节点下添加
dataBinding{
enabled=true
}
如:
数据绑定
在xml布局中使用dataBinding时,要将xml布局文件的节点进行修改,如:
将layout节点放最外面,接着是data节点,在data节点中可以新增variable节点,在data节点下面可以写之前的布局节点;
variable节点中name是type的一个名称,type是对应model类的包名路径,通过@{}的方式对TextView进行赋值;在activity文件中通过DataBindingUtil进行引用;
public class SimpleActivity extends AppCompatActivity {
private ActivitySimpleBinding binding;
private Employee employee = new Employee("Zhai", "Mark");
private Worker worker = new Worker("1111", "2222");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_simple);
//通过直接赋值
// binding.firstName.setText(employee.getFirstName());
// binding.lastName.setText(employee.getLastName());
// binding.setEmployee(employee);
binding.setVariable(BR.employee, employee);
}
}
ActivitySimpleBinding命名:布局文件名称(第一字母大写,遇到,去掉并将首字母大写)+Binding;
这里赋值有三种方式:
1、通过ActivitySimpleBinding.控件id,直接赋值;
如:binding.firstName.setText(employee.getFirstName());
2、ActivitySimpleBinding调用set方法;
如:binding.setEmployee(employee);
3、ActivitySimpleBinding调用setVariable方法;
binding.setVariable(BR.employee, employee);
事件绑定
事件可以通过方法或者监听器的形式来实现;
在对应的activity中声明一个事件的内部类,在内部类中定义对应的事件方法,在xml布局中通过新增variable节点,对它的name和type属性进行设置,也是用过@{}的方式绑定事件;
public class SimpleActivity extends AppCompatActivity {
private ActivitySimpleBinding binding;
private Employee employee = new Employee("Zhai", "Mark");
private Worker worker = new Worker("1111", "2222");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_simple);
binding.setEventlistener(new EventListener());
}
public class EventListener {
public void clickFirst(View view) {
worker.setFirstName("55555");
worker.setLastName("7777777");
Toast.makeText(SimpleActivity.this, "点击了并刷新worker对象的值", Toast.LENGTH_LONG).show();
}
public void btnClick(View view){
Toast.makeText(SimpleActivity.this, "监听器方式", Toast.LENGTH_LONG).show();
}
public boolean onLongClick(View view) {
Toast.makeText(SimpleActivity.this, "长按了", Toast.LENGTH_LONG).show();
return false;
}
}
}
activity中通过setEventlistener方法传入EventListener对象,setEventlistener方法是自动生成的就可以了;在定义事件方法名称时需要注意:方法的名称可以和之前写法不一样,但是方法名称的参数要和之前的写法一样,不然会报错,比如:clickFirst(View view)必须要有view参数,如果没有会报错
BaseObservable
BaseObservable在数据发生改变时通知xml布局试图进行刷新
public class Worker extends BaseObservable {
private String mLastName;
private String mFirstName;
private boolean mIsFired=false;
public Worker(String mLastName, String mFirstName) {
this.mLastName = mLastName;
this.mFirstName = mFirstName;
}
@Bindable
public String getLastName() {
return mLastName;
}
public void setLastName(String mLastName) {
this.mLastName = mLastName;
notifyPropertyChanged(BR.lastName);
}
@Bindable
public String getFirstName() {
return mFirstName;
}
public void setFirstName(String mFirstName) {
this.mFirstName = mFirstName;
notifyPropertyChanged(BR.firstName);
}
@Bindable
public boolean getFired() {
return mIsFired;
}
public void setFired(boolean mIsFired) {
this.mIsFired = mIsFired;
//刷新所有有关的ui
// notifyChange();
}
}
定义一个model类继承自BaseObservable,在get方法上添加@Bindable注解,在set方法中notifyPropertyChanged(BR.lastName)方法更新指定的视图,通过notifyChange();方法更新所有的视图,即使数据没有更新也会重新刷新视图;
name.xml
点击调用clickFirst方法时改变inclue 中name.xml中的视图;
public class SimpleActivity extends AppCompatActivity {
private ActivitySimpleBinding binding;
private Worker worker = new Worker("1111", "2222");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_simple);
binding.setEventlistener(new EventListener());
binding.setWorker(worker);
}
public class EventListener {
public void clickFirst(View view) {
worker.setFirstName("55555");
worker.setLastName("7777777");
Toast.makeText(SimpleActivity.this, "点击了并刷新worker对象的值", Toast.LENGTH_LONG).show();
}
}
}
在触发点击事件,重新对model进行赋值后,并没有做其他动作,视图就更新了;
include
在BaseObservable视图更新中其实就提到了include的使用了;
name.xml
activity中的逻辑和上面都差不多,这里就不说了;
ViewStub
有时候在项目开发中会使用ViewStub进行加载视图,看看在dataBinding中如何使用ViewStub;
viewstub.xml
和之前的使用一样没有区别;
RecyclerView
在开发过程中列表(RecyclerView,ListView,GridView等)的使用是比较频繁的,所有就来看看RecyclerView在dataBinding中是如何绑定数据的;
布局中和之前的写法一样,看看它adapter的写法有什么不同;
public class BindingViewHolder extends RecyclerView.ViewHolder {
private T mBinding;
public BindingViewHolder(@NonNull T binding) {
super(binding.getRoot());
mBinding = binding;
}
public T getBinding() {
return mBinding;
}
}
这了ViewHolder弄一个单独的类,没有将其弄成adapter的内部类了,实例化的时和以前不同的时传入dataBinding对象而不是View对象,然后通过getRoot方法去获取View对象;
public class EmployeeAdapter extends RecyclerView.Adapter {
private LayoutInflater mLayoutInflater;
private OnItemClickListener mListener;
private List mWorkList;
public interface OnItemClickListener {
void onItemClickListener(Worker worker);
}
public EmployeeAdapter(Context context) {
this.mLayoutInflater = LayoutInflater.from(context);
this.mWorkList = new ArrayList<>();
}
@NonNull
@Override
public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_work_on, viewGroup, false);
return new BindingViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull BindingViewHolder bindingViewHolder, int i) {
final Worker item = mWorkList.get(i);
ViewDataBinding binding = bindingViewHolder.getBinding();
binding.setVariable(BR.item, item);
binding.executePendingBindings();
bindingViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onItemClickListener(item);
}
}
});
}
@Override
public int getItemCount() {
return mWorkList.size();
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mListener = listener;
}
public void addAll(List workers) {
mWorkList.addAll(workers);
}
Random mRandom = new Random(System.currentTimeMillis());
public void add(Worker worker) {
int position = mRandom.nextInt(mWorkList.size() +1);
mWorkList.add(position,worker);
notifyItemInserted(mWorkList.size());
}
public void remove() {
if (mWorkList.size() == 0) {
return;
}
int position = mRandom.nextInt(mWorkList.size());
mWorkList.remove(position);
notifyItemRemoved(position);
}
}
在onCreateViewHolder方法中通过DataBindingUtil.inflate加载视图布局,并创建ViewDataBinding实例对象;onBindViewHolder方法中就不用像之前那样通过获取控件,然后对控件进行赋值,直接通过ViewHolder获取到对应的ViewDataBinding,然后调用setVariable对视图进行赋值,点击事件这些也可以按照之前的方式实现;
item_work_on.xml
public class RecyclerViewActivity extends AppCompatActivity {
private ActivityRecyclerviewBinding binding;
private EmployeeAdapter adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_recyclerview);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new EmployeeAdapter(this);
binding.recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new EmployeeAdapter.OnItemClickListener() {
@Override
public void onItemClickListener(Worker worker) {
Toast.makeText(RecyclerViewActivity.this, worker.getFirstName() + "--" + worker.getLastName(), Toast.LENGTH_LONG).show();
}
});
binding.setControllrecyclerview(new ControllRecyclerView());
List demoList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Worker worker = new Worker("Zhai", "Mark");
demoList.add(worker);
}
adapter.addAll(demoList);
}
public class ControllRecyclerView {
public void addItem(View view) {
Worker worker = new Worker("1111", "11111");
adapter.add(worker);
}
public void removeItem(View view) {
adapter.remove();
}
}
}
activity中直接通过dataBinding获取RecyclerView实例,然后设置LayoutMananger和adapter;这样就实现了列表数据的绑定,对于ListView和GridView列表数据的绑定差不多;
@BindingAdapter自定义属性
要将后台返回的url图片链接通过ImageView显示在界面上,需要通过第三方图片加载库将其加载显示出来,在dataBinding中xml布局中ImageView并不能通过@{}的方式加载url链接,那怎么来实现图片加载呢?通过@BindingAdapter自定义属性的方式可以实现;
public class DemoBindingAdapter {
@BindingAdapter({"app:imageUrl","app:placeholder"})
public static void loadImageFromUrl(ImageView view, String url, Drawable drawable){
Glide.with(view.getContext())
.load(url)
.placeholder(drawable)
.into(view);
}
}
定义一个loadImageFromUrl的静态方法,并给方法添加@BindingAdapter注解,在注解中声明一个imageUrl和placeholder,在xml中给imageUrl和placeholder进行赋值;
通过app:imageUrl和app:placeholder进行引用;
public class ExpressionActivity extends AppCompatActivity {
private ActivityExpressionBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= DataBindingUtil.setContentView(this,R.layout.activity_expression);
Employee employee=new Employee("111","3333");
employee.setAvatar("http://img.tupianzj.com/uploads/allimg/160728/9-160HP91408.jpg");
binding.setEmployee(employee);
}
}
双向绑定
双向绑定的意思就是将数据model的值显示到视图上面,model改变时自动更新视图,视图改变时自动更新model;在dataBinding中通过BaseObservable和@={}来实现,注意不是@{}(赋值),是@={}
public class FormModel extends BaseObservable {
private String userName;
private String passWord;
public FormModel() {
}
public FormModel(String userName, String passWord) {
this.userName = userName;
this.passWord = passWord;
}
@Bindable
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
notifyPropertyChanged(BR.userName);
}
@Bindable
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
notifyPropertyChanged(BR.passWord);
}
}
数据model的写法还是和BaseObservable视图自动更新写法一样的;
public class TwoWayActivity extends AppCompatActivity {
private ActivityTwoWayBinding binding;
private FormModel formmodel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= DataBindingUtil.setContentView(this,R.layout.activity_two_way);
formmodel=new FormModel("111","222");
binding.setFormmodel(formmodel);
binding.setCommitFrom(new CommitFrom());
formmodel.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
//监听发送改变
}
});
}
public class CommitFrom{
public void commitFrom(View view){
Toast.makeText(TwoWayActivity.this,formmodel.getUserName()+"----"+formmodel.getPassWord(),Toast.LENGTH_LONG).show();
}
}
}
在activity中还可以通过addOnPropertyChangedCallback来监听视图的改变;
这样就实现了数据和视图的双向绑定,在点击提交的时候并没有做其他操作,就只是做了一个Toast提示;
动画
接下来看下动画的简单实现;
需要注意:不管是不是动画的实现,如果在布局中通过三元运算符等操作运算符来控制视图的显示或者隐藏,需要在data节点标签中做引用;
public class AnimationActivity extends AppCompatActivity {
private ActivityAnimationBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= DataBindingUtil.setContentView(this,R.layout.activity_animation);
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
ViewGroup viewGroup= (ViewGroup) binding.getRoot();
TransitionManager.beginDelayedTransition(viewGroup);
return true;
}
});
binding.setPersenter(new Persenter());
}
public class Persenter{
public void onCheckedChanged(View buttonView, boolean isChanged){
binding.setShowImage(isChanged);
}
}
}
上面这些只是dataBinding的部分用法,更多的用法可以查阅更多的资料进行学习。
源码
来源:Android dataBinding 的绑定数据、列表、点击事件等使用