目录
Android Jetpack应用指南学习笔记12--DataBinding的简单使用
4.MainActivity代码:主要是绑定view和设置数据
8.1 添加一个事件工具类:EventHandlerListener
17.1 TwoWayBindingViewModelField类:
19.2 RecyclerViewBindingAdapter代码:
19.6 RecyclerViewActivity布局代码:
Android Jetpack应用指南学习笔记12--DataBinding的简单使用
1.DataBinding的简介
DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。使其维护起来更加方便,架构更明确简介。所谓的绑定,是绑定什么呢?
数据直接绑定到UI上,数据改变时UI自动更新 UI上的数据绑定到变量中,当数据(如EditText中的数据)改变时自动更新 DataBinding非常适合用于MVVM模式中充当View和ViewModel的双向通讯的工具,引入DataBinding之后,我们可以少写很多的例如findViewById()之类的代码。
2.引入依赖:
buildFeatures { viewBinding true dataBinding true }
3.实体类代码:
/** * @author: njb * @date: 2022/9/17 21:35 * @desc: */ public class Book { public String title; public String author; public int rating; public Book(String title,String author){ this.title = title; this.author= author; } }
4.MainActivity代码:主要是绑定view和设置数据
public class MainActivity extends AppCompatActivity { ActivityMainBinding activityMainBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); } private void initView() { activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main); Book book = new Book("AndroidJetPack应用指南学习之DataBinding简单使用","淡淡的香烟"); book.rating = 5; activityMainBinding.setBook(book); } }
5.主界面activity_main代码:
5.1首先是设置数据:
和之前的普通方式不一样,这里布局是以layout开始的,熟悉的小伙伴应该都知道.
<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" tools:context=".MainActivity"> <data> <import type="com.example.databindingdemo.utils.BookRatingUtils" /> <variable name="book" type="com.example.databindingdemo.bean.Book" /> </data>
5.2 在布局中设置数据:
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_title" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.title}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_content" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.author}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_title" /> <TextView android:id="@+id/tv_rating" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{BookRatingUtils.getRatingString(book.rating)}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_content" /> </androidx.constraintlayout.widget.ConstraintLayout>
5.3 布局完整代码如下:
<?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" tools:context=".MainActivity"> <data> <import type="com.example.databindingdemo.utils.BookRatingUtils" /> <variable name="book" type="com.example.databindingdemo.bean.Book" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_title" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.title}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_content" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.author}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_title" /> <TextView android:id="@+id/tv_rating" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{BookRatingUtils.getRatingString(book.rating)}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_content" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
6.评分工具类代码:
package com.example.databindingdemo.utils; /** * @author: njb * @date: 2022/9/17 21:52 * @desc: 评分工具类 */ public class BookRatingUtils { public static String getRatingString(int rat){ switch (rat){ case 0: return "非常不好看"; case 1: return "不好看"; case 2: return "有一点不好看"; case 3: return "有一点好看"; case 4: return "好看"; case 5: return "非常好看"; } return ""; } }
7.实现的效果如下:
8.DataBinding响应事件
8.1 添加一个事件工具类:EventHandlerListener
package com.example.databindingdemo.listener; import android.content.Context; import android.view.View; import android.widget.Toast; /** * @author: njb * @date: 2022/9/18 0:02 * @desc: */ public class EventHandlerListener { private Context context; public EventHandlerListener(Context context){ this.context = context; } public void onButtonClicked(View view){ Toast.makeText(context,"I am clicked!",Toast.LENGTH_SHORT).show(); } }
8.2 在布局中添加事件和监听
<data> <import type="com.example.databindingdemo.utils.BookRatingUtils" /> <variable name="book" type="com.example.databindingdemo.bean.Book" /> <variable name="EventHandler" type="com.example.databindingdemo.listener.EventHandlerListener" /> </data> <Button android:id="@+id/btn_test" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="40dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="Click me" android:textColor="@color/white" android:textSize="20sp" android:onClick="@{EventHandler.onButtonClicked}" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_rating" />
9.添加事件的监听和使用
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); initListener(); } private void initListener() { activityMainBinding.setEventHandler(new EventHandlerListener(this)); }
10.事件响应的效果如下:
11.添加自定义的BindingAdapter
/** * @author: njb * @date: 2022/9/18 0:36 * @desc: 自定义BindingAdapter */ public class BindingAdapterImage { @BindingAdapter("image") public static void setImage(ImageView image,int imgResource){ image.setImageResource(imgResource); } @BindingAdapter("netWorkImage") public static void setNetWorkImage(ImageView imageView,String imgUrl){ if(!TextUtils.isEmpty(imgUrl)){ Picasso.get().load(imgUrl).placeholder(R.drawable.ic_launcher_background) .error(R.mipmap.ic_launcher).centerCrop().resize(300,300) .into(imageView); }else { imageView.setBackgroundColor(Color.RED); } } @BindingAdapter("padding") public static void setPadding(View view,int oldPadding, int newPadding){ if(oldPadding != newPadding){ view.setPadding(newPadding,newPadding,newPadding,newPadding); } } }
12.自定义BindAdapter布局如下:
<?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="netWorkImage" type="String" /> <variable name="localImage" type="int" /> <variable name="imagePadding" type="int" /> <variable name="ClickHandler" type="com.example.databindingdemo.BindAdapterActivity.ClickHandler" /> </data> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/iv_local" android:layout_width="0dp" android:layout_height="50dp" app:image="@{localImage}" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="MissingConstraints" /> <ImageView android:id="@+id/iv_netWork" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="10dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/iv_local" app:netWorkImage="@{netWorkImage}" app:padding="@{imagePadding}" tools:ignore="MissingConstraints" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:onClick="@{ClickHandler.onClick}" android:text="change padding" android:textAllCaps="false" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/iv_netWork" tools:ignore="MissingConstraints" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> </layout>
13.BindAdapterActivity代码:
package com.example.databindingdemo; import android.os.Bundle; import android.view.View; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import com.example.databindingdemo.databinding.ActivityBindingAdapterBinding; /** * @author: njb * @date: 2022/9/18 0:41 * @desc: */ public class BindAdapterActivity extends AppCompatActivity { private ActivityBindingAdapterBinding bindingAdapterBinding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); } private void initView() { bindingAdapterBinding = DataBindingUtil.setContentView(this,R.layout.activity_binding_adapter); bindingAdapterBinding.setLocalImage(R.mipmap.ic_launcher); bindingAdapterBinding.setNetWorkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F013b3d5c04dc59a80121ab5d0776b2.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1666026424&t=89bb1c43518c696a2000ba442b9be1d6"); bindingAdapterBinding.setImagePadding(20); bindingAdapterBinding.setClickHandler(new ClickHandler()); } public class ClickHandler { public void onClick(View view){ bindingAdapterBinding.setImagePadding(120); } } }
14.自定义BindingAdapter效果如下:
改变Padding后的效果如下:
15.使用ViewModel实现双向绑定:
package com.example.databindingdemo.bean;
import android.util.Log;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import com.example.databindingdemo.BR;
/**
* @author: njb
* @date: 2022/9/18 16:27
* @desc: 使用ViewModel实现双向绑定
*/
public class TwoWayBindingViewModel extends BaseObservable {
private LoginModel loginModel;
public TwoWayBindingViewModel(){
loginModel = new LoginModel();
loginModel.userName = "JetPack";
}
@Bindable
public String getUserName(){
return loginModel.userName;
}
public void setUserName(String userName){
if(userName != null && !userName.equals(loginModel.userName)){
loginModel.userName = userName;
notifyPropertyChanged(BR.userName);
Log.d("--viewModel--",userName);
}
}
}
16.ViewModel布局和Activity如下:
16.1 ViewModel布局:
<?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="viewModel"
type="com.example.databindingdemo.bean.TwoWayBindingViewModel" />
<!-- <variable
name="viewModel"
type="com.example.databindingdemo.bean.TwoWayBindingViewModelField" />-->
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="0dp"
android:layout_height="60dp"
android:background="@color/colorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_margin="20dp"
android:text="@={viewModel.userName}"
android:textColor="@color/white"
android:textSize="20sp"
android:gravity="center"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
16.2 ViewModelActivity代码如下:
package com.example.databindingdemo;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.example.databindingdemo.bean.TwoWayBindingViewModel;
import com.example.databindingdemo.bean.TwoWayBindingViewModelField;
import com.example.databindingdemo.databinding.ActivityTwoWayBindingBinding;
/**
* @author: njb
* @date: 2022/9/18 16:30
* @desc:
*/
public class TwoWayBindingActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
ActivityTwoWayBindingBinding twoWayBindingBinding = DataBindingUtil.setContentView(this,R.layout.activity_two_way_binding);
twoWayBindingBinding.setViewModel(new TwoWayBindingViewModel());
//twoWayBindingBinding.setViewModel(new TwoWayBindingViewModelField());
}
}
17.使用ObservableField优化双向绑定:
17.1 TwoWayBindingViewModelField类:
package com.example.databindingdemo.bean;
import android.util.Log;
import androidx.databinding.BaseObservable;
import androidx.databinding.ObservableField;
/**
* @author: njb
* @date: 2022/9/18 16:42
* @desc: 使用ObservableField优化双向绑定
*/
public class TwoWayBindingViewModelField extends BaseObservable {
private ObservableField<LoginModel> loginModelObservableField;
public TwoWayBindingViewModelField() {
LoginModel loginModel = new LoginModel();
loginModel.userName = "Jack";
loginModelObservableField = new ObservableField<>();
loginModelObservableField.set(loginModel);
}
public void setUserName(String userName) {
loginModelObservableField.get().userName = userName;
Log.d("--fieldViewModel--", userName);
}
public String getUserName() {
return loginModelObservableField.get().userName;
}
}
17.2 布局中使用方式如下:
17.3 Activity中使用代码:
17.4 日志打印:
可以看到两种方式基本上差不多,使用ObservableField方式时通过ObservableField将LoginModel对象包装起来,并为对象写好Getter和Setter方法,在布局中依然通过@={}的方式完成双向绑定,运行程序发现getUserName方法在程序启动时被自动调用,当用户修改EditText中的内容时setUserName被自动调用,对于Getter方法也无需添加@Bindable,使用方便了许多.
18.双向绑定的运行效果图如下:
19.RecyclerView的双向绑定机制:
19.1 RecyclerViewAdapter:
package com.example.databindingdemo.adapter; 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.example.databindingdemo.R; import com.example.databindingdemo.bean.Book; import com.example.databindingdemo.databinding.LayoutItemBinding; import java.util.List; /** * @author: njb * @date: 2022/9/18 17:16 * @desc: */ public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> { private List<Book> bookList; public RecyclerViewAdapter(List<Book> books){ this.bookList = books; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutItemBinding layoutItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.layout_item,parent,false); return new MyViewHolder(layoutItemBinding); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { Book book = bookList.get(position); holder.layoutItemBinding.setBook(book); } @Override public int getItemCount() { return bookList.size(); } public class MyViewHolder extends RecyclerView.ViewHolder { LayoutItemBinding layoutItemBinding; public MyViewHolder(LayoutItemBinding itemView) { super(itemView.getRoot()); layoutItemBinding = itemView; } } }
19.2 RecyclerViewBindingAdapter代码:
package com.example.databindingdemo.adapter; import android.graphics.Color; import android.text.TextUtils; import android.widget.ImageView; import androidx.databinding.BindingAdapter; import com.example.databindingdemo.R; import com.squareup.picasso.Picasso; /** * @author: njb * @date: 2022/9/18 17:07 * @desc: */ public class RecyclerViewBindingAdapter { @BindingAdapter("itemImage") public static void setNetWorkImage(ImageView imageView, String imgUrl){ if(!TextUtils.isEmpty(imgUrl)){ Picasso.get().load(imgUrl).placeholder(R.drawable.ic_launcher_background) .error(R.mipmap.ic_launcher).centerCrop().resize(300,300) .into(imageView); }else { imageView.setBackgroundColor(Color.RED); } } }
19.3 RecyclerViewViewModel代码:
/** * @author: njb * @date: 2022/9/18 17:12 * @desc: */ public class RecyclerViewViewModel { public List<Book> getBooks(){ List<Book> books = new ArrayList<>(); for(int i=0;i<100;i++){ Book book = new Book(Constants.title,Constants.content); book.img = Constants.imgUrl; books.add(book); } return books; } }
19.4 layout_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"> <data> <variable name="book" type="com.example.databindingdemo.bean.Book" /> <variable name="EventHandler" type="com.example.databindingdemo.listener.EventHandlerListener" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_local" android:layout_width="140dp" android:layout_height="140dp" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:gravity="center" app:itemImage="@{book.img}" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_title" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:layout_marginEnd="20dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.title}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toRightOf="@+id/iv_local" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_content" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:layout_marginEnd="20dp" android:background="@color/colorPrimaryDark" android:gravity="center" android:text="@{book.author}" android:textColor="@color/white" android:textSize="20sp" app:layout_constraintLeft_toRightOf="@+id/iv_local" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_title" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
19.5 RecyclerViewActivity代码:
package com.example.databindingdemo.activity; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.recyclerview.widget.LinearLayoutManager; import com.example.databindingdemo.R; import com.example.databindingdemo.adapter.RecyclerViewAdapter; import com.example.databindingdemo.databinding.ActivityRecyclerBindingAdapterBinding; import com.example.databindingdemo.viewmodel.RecyclerViewViewModel; /** * @author: njb * @date: 2022/9/18 17:26 * @desc: */ public class RecyclerViewActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); } private void initView() { ActivityRecyclerBindingAdapterBinding recyclerBindingAdapterBinding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_binding_adapter); recyclerBindingAdapterBinding.recyclerView.setHasFixedSize(true); RecyclerViewAdapter adapter = new RecyclerViewAdapter(new RecyclerViewViewModel().getBooks()); recyclerBindingAdapterBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerBindingAdapterBinding.recyclerView.setAdapter(adapter); } }
19.6 RecyclerViewActivity布局代码:
<?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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>