MVP和MVVM中,ViewModel与Presenter很类似,只不过ViewModel和View层多了双向绑定,当ViewModel中数据更改,View层能够知道,反之,View层数据改变,ViewModel也能够改变。ViewModel中LiveData可以被观测,进而在Activity中观测到值发生变化来更新ui,结合DataBinding,我们可以直接在xml中设置ViewModel,进一步简化逻辑,使我们的MVVM构架更加低耦合
一、初识DataBinding
DataBinding是一个可以将xml转换为对象的工具,我们知道对象的属性是可以赋值的,所以我们可以直接只用变量来赋值xml上的属性,使得它更据灵活性
1.在xml中使用实体类
在gradle中添加DataBinding支持
defaultConfig{
...dataBinding {enabled = true}
}
定义一个实体类,我们将把它放入布局文件
package com.aruba.databinding;/*** Created by aruba on 2021/9/11.*/
public class Idol {public String name;public String star;public Idol(String name, String star) {this.name = name;this.star = star;}
}
在将xml转换后,在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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="104dp"android:text="点赞"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"tools:text="appear" /><ImageViewandroid:id="@+id/imageView"android:layout_width="200dp"android:layout_height="200dp"android:src="@drawable/sijiali"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.248"tools:srcCompat="@tools:sample/avatars" /><TextViewandroid:id="@+id/textView2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{idol.name}"android:textSize="30sp"app:layout_constraintBottom_toTopOf="@+id/button"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/imageView"app:layout_constraintVertical_bias="0.232"tools:text="name" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{idol.star}"android:textSize="24sp"app:layout_constraintBottom_toTopOf="@+id/button"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView2"app:layout_constraintVertical_bias="0.244"tools:text="Star" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="idol"type="com.aruba.databinding.Idol" /></data>
</layout>
Activity中使用DataBinding将布局转换成对象
package com.aruba.databinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;import android.os.Bundle;import com.aruba.databinding.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);activityMainBinding.setIdol(new Idol("斯嘉丽·约翰逊", "五星"));}
}
效果:
2.xml中调用函数
如果我们的实体类中star属性为int型,有一个静态全局函数根据传入的star返回一个字符串,那么怎么调用它
package com.aruba.databinding;/*** Created by aruba on 2021/9/11.*/
public class Idol {public String name;public int star;public Idol(String name, int star) {this.name = name;this.star = star;}
}
package com.aruba.databinding;/*** Created by aruba on 2021/9/11.*/
public class StarUtils {public static String getStar(int star) {String starStr = null;switch (star) {case 1:starStr = "一星";break;case 2:starStr = "二星";break;case 3:starStr = "三星";break;case 4:starStr = "四星";break;case 5:starStr = "五星";break;}return starStr;}
}
在xml中使用import标签导入工具类,在相应的地方调用工具类方法
<?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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="104dp"android:text="点赞"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"tools:text="appear" /><ImageViewandroid:id="@+id/imageView"android:layout_width="200dp"android:layout_height="200dp"android:src="@drawable/sijiali"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.248"tools:srcCompat="@tools:sample/avatars" /><TextViewandroid:id="@+id/textView2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{idol.name}"android:textSize="30sp"app:layout_constraintBottom_toTopOf="@+id/button"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/imageView"app:layout_constraintVertical_bias="0.232"tools:text="name" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{StarUtils.getStar(idol.star)}"android:textSize="24sp"app:layout_constraintBottom_toTopOf="@+id/button"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView2"app:layout_constraintVertical_bias="0.244"tools:text="Star" /></androidx.constraintlayout.widget.ConstraintLayout><data><import type="com.aruba.databinding.StarUtils" /><variablename="idol"type="com.aruba.databinding.Idol" /></data>
</layout>
我们还要想要给点赞button调用一个对象的方法,定义对象如下:
package com.aruba.databinding;import android.content.Context;
import android.view.View;
import android.widget.Toast;/*** Created by aruba on 2021/9/11.*/
public class OnClickHandler {private Context context;public OnClickHandler(Context context) {this.context = context;}public void onClick(View view) {Toast.makeText(context, "点赞", Toast.LENGTH_SHORT).show();}
}
xml中加入,并调用函数
<data><import type="com.aruba.databinding.StarUtils" /><variablename="idol"type="com.aruba.databinding.Idol" /><variablename="onClickHandler"type="com.aruba.databinding.OnClickHandler" /></data><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="104dp"android:onClick="@{onClickHandler.onClick}"android:text="点赞"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"tools:text="appear" />
Activty中修改传入的参数类型和点击事件对象后,效果如下:
3.传递对象至include标签
有时我们会使用include标签封装一些布局,那么这些布局怎么拿到主布局中定义的对象呢
首先定义include布局,转换成DataBing布局
<?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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:paddingVertical="10dp"><TextViewandroid:id="@+id/textView2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:text="@{idol.name}"android:textSize="30sp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"tools:text="name" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="36dp"android:text="@{StarUtils.getStar(idol.star)}"android:textSize="24sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView2"tools:text="Star" /></androidx.constraintlayout.widget.ConstraintLayout><data><import type="com.aruba.databinding.StarUtils" /><variablename="idol"type="com.aruba.databinding.Idol" /></data>
</layout>
Activity的xml中引入include布局
<?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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="104dp"android:onClick="@{onClickHandler.onClick}"android:text="点赞"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"tools:text="appear" /><ImageViewandroid:id="@+id/imageView"android:layout_width="200dp"android:layout_height="200dp"android:src="@drawable/sijiali"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.248"tools:srcCompat="@tools:sample/avatars" /><includelayout="@layout/include_layout_info"android:layout_width="0dp"android:layout_height="wrap_content"app:idol="@{idol}"app:layout_constraintBottom_toTopOf="@+id/button"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/imageView"app:layout_constraintVertical_bias="0.32" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="idol"type="com.aruba.databinding.Idol" /><variablename="onClickHandler"type="com.aruba.databinding.OnClickHandler" /></data>
</layout>
只需要改xml就可以了,非常方便
效果:
4.自定义BindingAdapter
虽然我们可以在xml调用函数,但只能针对一些简单逻辑,如果需要加载网络图片,并实现更复杂的逻辑代码,那么使用BindingAdapter是实现它们的好工具,它支持我们自定义一个属性,并实现相应的方法
定义一个类,使用BindingAdapter注解:
package com.aruba.databinding;import android.widget.ImageView;import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;/*** Created by aruba on 2021/9/11.*/
public class ImageViewBindingAdapter {@BindingAdapter("image")public static void setImage(ImageView imageView, String url) {}
}
这样我们就可以在xml中使用image了,我们使用image传入一个变量,界面渲染后,DataBinding会自动调用上面定义的setImage方法
<variablename="imageUrl"type="String" /><ImageViewandroid:id="@+id/imageView"android:layout_width="200dp"android:layout_height="200dp"app:image="@{imageUrl}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.248"tools:srcCompat="@tools:sample/avatars" />
在Activity中传入这个imageUrl变量
activityMainBinding.setImageUrl("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80");
我们再对setImage方法进行加工处理
package com.aruba.databinding;import android.text.TextUtils;
import android.widget.ImageView;import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;import com.squareup.picasso.Picasso;/*** Created by aruba on 2021/9/11.*/
public class ImageViewBindingAdapter {@BindingAdapter("image")public static void setImage(ImageView imageView, String url) {if (!TextUtils.isEmpty(url)) {Picasso.get().load(url).placeholder(R.drawable.sijiali).into(imageView);} else {imageView.setImageResource(R.drawable.sijiali);}}
}
BindingAdapter也支持重载
package com.aruba.databinding;import android.text.TextUtils;
import android.widget.ImageView;import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;import com.squareup.picasso.Picasso;/*** Created by aruba on 2021/9/11.*/
public class ImageViewBindingAdapter {@BindingAdapter("image")public static void setImage(ImageView imageView, String url) {if (!TextUtils.isEmpty(url)) {Picasso.get().load(url).placeholder(R.drawable.sijiali).into(imageView);} else {imageView.setImageResource(R.drawable.sijiali);}}@BindingAdapter("image")public static void setImage(ImageView imageView, int resId) {imageView.setImageResource(resId);}@BindingAdapter(value = {"image", "default"}, requireAll = false)public static void setImage(ImageView imageView, String url, int resId) {if (!TextUtils.isEmpty(url)) {Picasso.get().load(url).placeholder(resId).into(imageView);} else {imageView.setImageResource(resId);}}
}
二、双向绑定
DataBinding支持双向绑定,前面我们实现了单向绑定,当变量值发生变化,那么控件上也会更新,双向绑定和单向绑定对比,新增了如果控件中属性的值发生变化,那么变量的值也会发生变化
1.BaseObservable
首先定义一个实体类
package com.aruba.databinding2;/*** Created by aruba on 2021/9/11.*/
public class User {public String userName;public User(String userName) {this.userName = userName;}
}
再定义一个ViewModel继承至BaseObservable,并在想要双向绑定的方法上加上@Bindable注解,那么相应的get和set方法就会被自动调用,当值发生变化时,调用set方法,我们还可以通过notifyPropertyChanged方法来通知绑定get方法的控件重新执行get方法
package com.aruba.databinding2;import android.util.Log;
import android.widget.Toast;import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;/*** Created by aruba on 2021/9/11.*/
public class UserViewModel extends BaseObservable {private static final String TAG = UserViewModel.class.getSimpleName();private User user;public UserViewModel() {user = new User("张三");}@Bindablepublic String getUserName() {return user.userName;}public void setUserName(String userName) {if (userName != null && !userName.equals(getUserName())) {user.userName = userName;notifyPropertyChanged(BR.userName);Log.i(TAG, userName);}}
}
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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.appcompat.widget.AppCompatEditTextandroid:layout_width="300dp"android:layout_height="wrap_content"android:minHeight="48dp"android:text="@={userViewModel.userName}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="userViewModel"type="com.aruba.databinding2.UserViewModel" /></data>
</layout>
最后在Activity中绑定ViewModel
package com.aruba.databinding2;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;import android.os.Bundle;import com.aruba.databinding2.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity {private UserViewModel userViewModel;private ActivityMainBinding activityMainBinding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);userViewModel = new UserViewModel();activityMainBinding.setUserViewModel(userViewModel);}
}
效果:
日志打印:
2021-09-11 12:41:07.601 5811-5811/com.aruba.databinding2 I/UserViewModel: 张
2021-09-11 12:41:08.854 5811-5811/com.aruba.databinding2 I/UserViewModel: h
2021-09-11 12:41:09.060 5811-5811/com.aruba.databinding2 I/UserViewModel: hh
2021-09-11 12:41:09.834 5811-5811/com.aruba.databinding2 I/UserViewModel: hhu
2021-09-11 12:41:10.550 5811-5811/com.aruba.databinding2 I/UserViewModel: hhut
2.ObservableField
除了继承BaseObservable外,还可以使用ObservableField对象,重新定义一个ViewModel,ObservableField不需要加注解了,使用方便很多
package com.aruba.databinding2;import android.util.Log;import androidx.databinding.ObservableField;/*** Created by aruba on 2021/9/11.*/
public class UserViewModel2 {private static final String TAG = UserViewModel2.class.getSimpleName();private ObservableField<User> userObservableField;public UserViewModel2() {User user = new User("赵四");userObservableField = new ObservableField<>(user);}public String getUserName() {return userObservableField.get().userName;}public void setUserName(String userName) {Log.i(TAG,userName);userObservableField.get().userName = userName;}
}
布局中运用变量
<?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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.appcompat.widget.AppCompatEditTextandroid:id="@+id/appCompatEditText"android:layout_width="300dp"android:layout_height="wrap_content"android:minHeight="48dp"android:text="@={userViewModel.userName}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.appcompat.widget.AppCompatEditTextandroid:layout_width="300dp"android:layout_height="wrap_content"android:minHeight="48dp"android:text="@={userViewModel2.userName}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintHorizontal_bias="0.495"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/appCompatEditText"app:layout_constraintVertical_bias="0.133" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="userViewModel"type="com.aruba.databinding2.UserViewModel" /><variablename="userViewModel2"type="com.aruba.databinding2.UserViewModel2" /></data>
</layout>
Activity中传入绑定后,效果:
日志打印:
2021-09-11 12:50:39.530 6277-6277/com.aruba.databinding2 I/UserViewModel2: 赵
2021-09-11 12:50:40.634 6277-6277/com.aruba.databinding2 I/UserViewModel2: b
2021-09-11 12:50:41.292 6277-6277/com.aruba.databinding2 I/UserViewModel2: bf
2021-09-11 12:50:41.457 6277-6277/com.aruba.databinding2 I/UserViewModel2: bff
2021-09-11 12:50:42.560 6277-6277/com.aruba.databinding2 I/UserViewModel2: bffy
三、RecyclerView的绑定
定义实体类
package com.aruba.databinding3;/*** Created by aruba on 2021/9/11.*/
public class Idol {public String chName;public String enName;public String image;public Idol(String chName, String enName, String image) {this.chName = chName;this.enName = enName;this.image = image;}
}
定义item的布局文件
<?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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:paddingVertical="10dip"><ImageViewandroid:id="@+id/imageView"android:layout_width="100dip"android:layout_height="100dip"app:image="@{idol.image}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline2"app:layout_constraintHorizontal_bias="0.432"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.054"tools:srcCompat="@tools:sample/avatars" /><TextViewandroid:id="@+id/textViewChName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{idol.chName}"android:textSize="24sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.138"app:layout_constraintStart_toStartOf="@+id/guideline2"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.063"tools:text="斯嘉丽.约翰逊" /><TextViewandroid:id="@+id/textViewEnName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="28dp"android:text="@{idol.enName}"android:textSize="18sp"app:layout_constraintStart_toStartOf="@+id/textViewChName"app:layout_constraintTop_toBottomOf="@+id/textViewChName"tools:text="Scarlett Johansson" /><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/guideline2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent="0.4" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="idol"type="com.aruba.databinding3.Idol" /></data>
</layout>
定义adapter,转换databinding对象和Activity中稍有不同
package com.aruba.databinding3;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;import com.aruba.databinding3.databinding.ItemBinding;import java.util.List;/*** Created by aruba on 2021/9/11.*/
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {private List<Idol> datas;public RecyclerViewAdapter(List<Idol> datas) {this.datas = datas;}@NonNull@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {ItemBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);return new MyViewHolder(inflate);}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {Idol idol = datas.get(position);holder.itemBinding.setIdol(idol);}@Overridepublic int getItemCount() {return datas.size();}static class MyViewHolder extends RecyclerView.ViewHolder {private ItemBinding itemBinding;public MyViewHolder(ItemBinding itemBinding) {super(itemBinding.getRoot());this.itemBinding = itemBinding;}public MyViewHolder(@NonNull View itemView) {super(itemView);}}
}
Activity中配置RecyclerView
package com.aruba.databinding3;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;import android.os.Bundle;import com.aruba.databinding3.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity {private RecyclerViewAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);adapter = new RecyclerViewAdapter(IdolUtils.get());activityMainBinding.recyclerview.setAdapter(adapter);activityMainBinding.recyclerview.setLayoutManager(new LinearLayoutManager(this));}
}
效果:
四、DataBinding+ViewModel+LiveData组合使用
我们现在将最开始的明星界面完全使用ViewModel代替xml中的变量,并新增了分数,我们点击点赞时,分数+1
首先实体类如下:
package com.aruba.databingviewmodellivedata;import androidx.lifecycle.MutableLiveData;/*** Created by aruba on 2021/9/11.*/
public class Idol {private MutableLiveData<String> name;private MutableLiveData<Integer> star;private MutableLiveData<String> imageUrl;private MutableLiveData<Integer> score;public Idol(String name, int star, String imageUrl,int score) {this.name = new MutableLiveData<>();this.name.setValue(name);this.star = new MutableLiveData<>();this.star.setValue(star);this.imageUrl = new MutableLiveData<>();this.imageUrl.setValue(imageUrl);this.score = new MutableLiveData<>();this.score.setValue(score);}public MutableLiveData<String> getName() {return name;}public MutableLiveData<Integer> getStar() {return star;}public MutableLiveData<String> getImageUrl() {return imageUrl;}public void setName(String name) {this.name.setValue(name);}public void setStar(Integer star) {this.star.setValue(star);}public void setImageUrl(String imageUrl) {this.imageUrl.setValue(imageUrl);}public MutableLiveData<Integer> getScore() {return score;}public void setScore(Integer score) {this.score.setValue(score);}
}
定义ViewModel,注意要实现双向绑定,返回的是LiveData对象
package com.aruba.databingviewmodellivedata;import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;/*** Created by aruba on 2021/9/11.*/
public class IdolViewModel extends ViewModel {private Idol idol;public IdolViewModel() {}public void setIdolMutableLiveData(Idol idol) {if (idol == null) throw new NullPointerException("idol can not be null");this.idol = idol;}public MutableLiveData<String> getIdolName() {return idol.getName();}public MutableLiveData<Integer> getIdolStar() {return idol.getStar();}public MutableLiveData<String> getIdolImageUrl() {return idol.getImageUrl();}public MutableLiveData<Integer> getScore() {return idol.getScore();}public void addScore(int add) {idol.setScore(idol.getScore().getValue() + add);}
}
在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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="32dp"android:onClick="@{()->idolViewModel.addScore(2)}"android:text="点赞"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/include"tools:text="appear" /><ImageViewandroid:id="@+id/imageView"android:layout_width="200dp"android:layout_height="200dp"app:image="@{idolViewModel.getIdolImageUrl()}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.497"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.08"tools:srcCompat="@tools:sample/avatars" /><includeandroid:id="@+id/include"layout="@layout/include_layout_info"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="24dp"app:idolViewModel="@{idolViewModel}"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/imageView" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="idolViewModel"type="com.aruba.databingviewmodellivedata.IdolViewModel" /></data>
</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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:paddingVertical="10dp"><TextViewandroid:id="@+id/textView2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:text="@{idolViewModel.getIdolName()}"android:textSize="30sp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"tools:text="name" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="12dp"android:text="@{StarUtils.getStar(idolViewModel.getIdolStar())}"android:textSize="24sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView2"tools:text="Star" /><TextViewandroid:id="@+id/textView3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="12dp"android:text="@{String.valueOf(idolViewModel.getScore())}"android:textSize="30sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView"tools:text="score" /></androidx.constraintlayout.widget.ConstraintLayout><data><variablename="idolViewModel"type="com.aruba.databingviewmodellivedata.IdolViewModel" /><import type="com.aruba.databingviewmodellivedata.StarUtils"/></data>
</layout>
Activity中配置
package com.aruba.databingviewmodellivedata;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;import com.aruba.databingviewmodellivedata.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity {private ActivityMainBinding activityMainBinding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);IdolViewModel idolViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(IdolViewModel.class);Idol idol = new Idol("斯嘉丽·约翰逊", 4, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80",10);idolViewModel.setIdolMutableLiveData(idol);activityMainBinding.setIdolViewModel(idolViewModel);activityMainBinding.setLifecycleOwner(this);}
}
效果: