之前比較熟練使用MVP的框架,由於前段時間幫前端寫了段時間的vue,發現雙向綁定真的用的很方便,早就聽說MVVM也是雙向綁定的,用着很方便,再也不需要編寫findViewById,其實就是一個數據綁定框架,我就我現在的狀態也是一邊學一邊寫,從0到框架的使用,也算做個記錄吧
Ⅰ.DataBinding的準備工作
Ⅱ.DataBinding中實現對象,List和Map集合,數組等綁定和字段綁定
Ⅲ.使用DataBinding實現RecyclerView的綁定
Ⅳ.數據的更新操作
Ⅰ.以下是DataBinding的準備工作
1.在app的build.gradle的android模塊中添加
android {
dataBinding {
enabled = true
}
}
2.在project的build.gradle中的allprojects標籤下添加mavenCentral()
allprojects {
repositories {
mavenCentral()
}
}
3.以往我們在寫佈局的時候會直接引用android佈局裏面的LinearLayout或者RelativeLayout等,但是我們使用binding的時候需要在根節點添加小寫的layout類似下圖
注意:binding類是根據xml佈局文件的名字生成的,最後別忘記了Sync Now
activity_main.xml->ActivityMainBinding
activity_item.xml->ActivityItemBinding
splash_test.xml->SplashTestBinding
依此類推
Ⅱ.現在開始第二步 DataBinding中實現對象,List和Map集合,數組等綁定和字段綁定
1.我們首先可以創建一個對象Student
public class Student {
private int grade; //年級
private int age; //年齡
private String name;//名字
private int score; //分數
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
2.我們創建一個普通佈局(注意,如果字段爲int字段需要轉爲String)
<?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="student"
type="com.huaweidun.gpuimageapp.Student" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".StudentActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{student.name}"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(student.age)}"
/>
</LinearLayout>
</layout>
那麼問題來了,如果我想讓這兩個字段拼接在一起要怎麼做呢,直接相加,我們這裏還支持表達式,三元表達式,是不是很方便呢
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{"哦豁"+student.name+student.age}'
android:visibility="@{age>10?View.VISIBLE:View.GONE}"
/>
如果還有別的字段,我們可以在String.xml內拼接,等等還有很多其他格式化和替換拼接的,可自行百度
<resources>
<string name="app_name">TestApp</string>
<string name="custom_str">我的名字叫:%s,我年齡:%s</string>
</resources>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{@string/custom_str(student.name,student.age)}'
/>
運行效果
引入複雜變量,例如Map,List,數組,單個字段,接下來我們就使用一下吧,這裏的<是小於號>是大於號,這裏是需要轉義的,要不然可能會出現編譯錯誤
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="java.util.List"/>
<import type="java.util.Map"/>
<variable
name="list"
type="List<String>" />
<variable
name="map"
type="Map<String,Object>" />
<variable
name="array"
type="String[]" />
</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="@{list.get(0)}"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{map[`key`]}"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{array[0]}"
/>
</LinearLayout>
</layout>
然後我們在activity裏面進行設置這些變量,就可以顯示了
listString.add("string1");
listString.add("String2");
binding.setList(listString);
map.put("key1","key值1");
map.put("key2","key值2");
binding.setMap(map);
String[] arr={"字符串1","字符串2"};
binding.setArray(arr);
Ⅲ.好了,binding用法的基礎用法我們基本上可以用了,我們可以呢嘗試用databinding的方法寫一個recyclerview的綁定
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
首先在佈局裏面添加上recyclerview的佈局,接下來該我們去寫適配器了,其實跟我們寫界面時候是一樣的,item的佈局名字+binding進行相應引用
public class MultiItemAdapter extends RecyclerView.Adapter<MultiItemAdapter.ViewHolder> {
private MyItemClickListener itemClickListener;
private ItemUserListBinding binding;
private LayoutInflater layoutInflater;
private List<UserBean> userBeans;
private Context mContext;
private int variableId;
public MultiItemAdapter(Context context, List<UserBean> list,int variableId){
this.layoutInflater=LayoutInflater.from(context);
this.userBeans=list;
this.mContext=context;
this.variableId=variableId;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
binding= DataBindingUtil.inflate(layoutInflater,R.layout.item_user_list,parent,false);
ViewHolder viewHolder=new ViewHolder(binding.getRoot(),itemClickListener);
viewHolder.setBinding(binding);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.getBinding().setVariable(variableId,userBeans.get(position));
holder.getBinding().executePendingBindings();
}
@Override
public int getItemCount() {
return userBeans.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public MyItemClickListener mMyItemClickListener;
private ViewDataBinding binding;
public ViewHolder(@NonNull View itemView,MyItemClickListener myItemClickListener) {
super(itemView);
this.mMyItemClickListener=myItemClickListener;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(mMyItemClickListener!=null){
mMyItemClickListener.onItemClick(v,getPosition());
}
}
public ViewDataBinding getBinding(){
return binding;
}
public void setBinding(ViewDataBinding binding){
this.binding=binding;
}
}
public interface MyItemClickListener{
void onItemClick(View view,int position);
}
public void setOnItemClicklistener(MyItemClickListener myItemClickListener){
this.itemClickListener=myItemClickListener;
}
}
下面是我們的佈局文件item_user_list文件
<?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="user"
type="com.huaweidun.gpuimageapp.UserBean" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
/>
<TextView
android:id="@+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}"
/>
</LinearLayout>
</layout>
然後在界面中去填充我們的適配器,BR不用管,相當於我們的R文件是自動生成的
private void showRecycler(){
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
binding.rv.setLayoutManager(linearLayoutManager);
//設置適配器
MultiItemAdapter multiItemAdapter=new MultiItemAdapter(this,listUser,BR.user);
binding.rv.setAdapter(multiItemAdapter);
multiItemAdapter.setOnItemClicklistener(new MultiItemAdapter.MyItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this,listUser.get(position).getUserName(),Toast.LENGTH_SHORT).show();
}
});
}
Ⅳ.數據的更新操作
之前我們的數據更新的時候都是settext等,當我們的數據發生變化的時候,需要手動設置數據源,從而使得UI界面發生改變
把我們的實體類去繼承BaseObservable,然後在get上面添加@Bindable註解,在set中添加通知更新就可以了
public class Article extends BaseObservable {
private String content;
public Article (String content) {
this.content = content;
}
@Bindable
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
notifyPropertyChanged(BR.content);
}
然後當我們的數據發生變化的時直接使用article.setContent("我發生了改變")即可