DataBingding这个东西是15年推出的一个框架,去年尝试使用了一下,感觉不是很好用,包括IDE的兼容也不是很好,所以最后放弃,一年左右没有动过也忘得差不多了,不过最近谷歌退出了一个新的架构模式AAC(Android Architecture Components),研究了一下发现这个搭配DataBingding一起使用简直很美味啊,包括官方的Demo也是这么干的,所以重新学习了一下该框架,在此做个总结加深印象,至于AAC就不多说,还没有完全摸透,官方链接:https://developer.android.google.cn/develop/index.html
一,DataBingding好处
1,moudle与ViewD的几乎完全解耦
2,节省大量无脑力代码,包括findId,adapter,setOnClick()....等
3,据说布局的性能优于findviewbyId
4,结合AAC对app的生命周期的一系列操作更加便捷
.....
当然,有好处必然也会有一些坏处
1,xml的赋值方法,对于不熟悉,或者不是很熟悉的人来说,代码阅读起来会很头疼,代码量大的时候会显得很混乱,除了databingding常用者,或者代码开发者来说,比较恶心
2,个人感觉出现问题不是很好定位(比如说偶尔的xml忘记导包,导致编译错误等等)
3,IDE偶尔会抽风
不过综合起来,如果你已经熟练使用DataBingding,个人觉的马上行动起来,他的好处远远大于他的坏处.
二,基本使用
此类教程网上N多,多少无义,直接来到正题,
你需要在你要使用的这个Acitivity或者fragment的布局最外层嵌套一层layout,然后在laout正配置你需要用到的一系列东西,如下代码,这个是布局是一个列表的item,关于列表稍后再说,xml中配置相关的说明在下面代码中已经做了详细说明,请注意注释的文字说明.
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
//将你N多配置写在data标签下
<data>
/**
import即使字面意思导包的意思,这个很常用,具体有俩种理解吧:
1,比如说如果你有一个实体类叫做User,但是那你在下面需要配置很多个User的变量user1,user2....
你也可以每个变量都写上这个User的全路径,不过这么做很不好,建议直接导包.然后在创建变量的时候Type
写上这个类名即可,当然如果类名有重复,我们是课已自己定义一个别名的,在你导入的包的后面加上
alias="user"即可
2,比方说如下的textView你要使用一个系统的东西Color,你如果不导包的话编译是会出错的,所以你使用系统的
必须导包(这种情况有很多,Color,View等等...),当然有个包是例外的,就是java的lang包,如Srting类...
*/
<import type="android.graphics.Color"/>
/**
这个就像他的字面意思,变量,variable是你为这个xml定义的一个个变量.如下,你需要给它起一个名字name,
然后你需要为他指定一个type,这个type可以使你上面导包的类名,也可以是一个类的全类名,也可是是你导包
时候设置的别名,variable支持后多类型,比如String,int.....
*/
<variable
name="user"
type="luo.com.shiyu.bingdingdemo.User"/>
<variable
name="position"
type="Integer"/>
<variable
name="str"
type="String"/>
<variable
name="error"
type="boolean"/>
<variable
name="num"
type="int" />
<variable
name="list"
type="ArrayList<String>" />
<variable
name="map"
type="HashMap<String, String>" />
<variable
name="array"
type="String[]" />
/**
下面这些是某博客说定义list,map的key,其实这写也只是一个变量,和上面的没有任何区别,你把它当作key
它就是key,你把它当作一个int变量或者一个String变量都是可以的,也可以这么理解,list的索引是一个int,
所以我们定义一个int,起个名字叫做index而已,没有别的.
*/
<variable
name="listKey"
type="int" />
<variable
name="mapKey"
type="String" />
<variable
name="arrayKey"
type="int" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
/**
关于使用,就是这种固定的写法,很多文章都讲解的非常细致,包括一些三元表达式等...需要的可以自行查阅,
基本java代码中的一些表达写法都可以使用,当然不可已new,或者你调用的方法的返回值必须和这个xml属性匹配
*/
android:text="@{user.firstName}"
android:textColor="@{position%2==0?Color.RED:Color.BLUE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</layout>
三,关于xml中的说明基本就是上面这么多,下面是几点注意事项
1,@{user.firstName} 像这种情况,其实是调用的user的getFirstName,所以,在你不实用ObservableField这些包装类的情况下你必须要重写getFirstName
2,同理你如果要使用一个方法的话,这个方法也必须是public修饰的,使用方法有俩中写法, android:onClick="@{user.show}",像这样的,这个方法必须没有参数,或者有参数View,有参数View的一般都是绑定事件,多用在点击事件中.android:onClick="@{user.show()}"这也是一种写法,就是把函数后面的俩括号加上,而且方法是可以传递参数的
3,android:text='@{user.firstName+"liudehua"}' 如果你要在表达式中引入一个字符串的时候,请这样写,注意引号.
在java代码中的用法
系统会自动为我们生成一个根据你这个Activity或者fragment名字的ViewDataBinding的子类,也就是下面代码中的ActivityMainBinding,然后我们就可以获取数据(网络获取,数据库获取等等)然后调用setUser设置我们xml中定义的变量,系统会根据你xml中定义的variable 的name属性生成一堆set方法,你可以更具自己情况去设置.
ActivityMainBinding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
user.setFirstName("1111111111");
viewDataBinding.setUser(user);
当我们调用set方法的时候,xml中相对应的值就会改变,然后ui也会发生变化,当然你也可以根据id来设置,只需要为我们的控件设置一个id,viewDataBinding 中就会保存一个这个id的变量,你可以做一些操作
四,然后此时基本就可以使用databingding了,不过之前我们有一个概念叫做双向绑定,如何做到呢?现在的情况是我们把一个user设置给bingding,但是如果我们值设置一次,之后的user的值无论如何变化都是不会改变ui的,这个时候谷歌爸爸为我们提供了几个东西:
BaseObservable
我们可以通过Observable的方式去通知UI数据已经改变了,当然了,官方为我们提供了更加简便的方式BaseObservable,我们的实体类只需要继承该类,稍做几个操作,就能轻松实现数据变化的通知。如何使用呢? 首先我们的实体类要继承BaseObservale类,第二步在Getter上使用注解@Bindable,第三步,在Setter里调用方法notifyPropertyChanged,第四步,完成。就是这么简单,下面我们来实际操作一下。
首先定义一个实体类,并继承BaseObservable
public class User extends BaseObservable {
private String firstName;
public User () {
}
public Student(String name) {
this.name = name;
}
@Bindable
public String getFirstName() {
return name;
}
public void FirstName(String name) {
this.firstName= firstName;
//BR是类是java的R文件的一个东西,它保存有我们所有字段
notifyPropertyChanged(BR.firstName);
}
}
ObservableFields家族
上面使用BaseObservable已经非常容易了,但是google工程师还不满足,继续给我们封装了一系列的ObservableFields,这里有ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableArrayMap,ObservableArrayList,ObservableParcelable
ObservableFields的使用方法就更加简单了
public class User {
public ObservableField<String> firstName=new ObservableField<>();
public ObservableInt age = new ObservableInt();
}
这里我们就不用重写get方法了,因为内部已经替我们实现了,而且也不用我们调用notifyPropertyChanged了,个人建议使用这中方式,第一中的代码入侵太大.
五,recycleview的用法
这个东西说实话在listview中实在很好用,我们直接来看怎们用,首先来一个布局,就是我们开局时候的布局代码,
然后来看adapter怎们用:
1,onCreateViewHolder方法中我们来搞一个ViewDataBinding 出来
2,然后正常的写一个holder,
3,让这个holder保存一个ViewDataBinding ,因为这个holder是列表item的holder,所以保存的也是item布局的ViewDataBinding ,布局不匹配是会出大问题的.
4,为这个holder的ViewDataBinding 创建get,set方法方便获取设置
5,在onBindViewHolder中设置布局中需要的变量
6,最后在onBindViewHolder中调用 bingDing.executePendingBindings(),次方法是立即刷刷新界面,正常情况是在下一帧刷新.
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.MainHolder>{
private Context context;
private ArrayList<User> list=new ArrayList<>();
public MainAdapter(Context context) {
this.context = context;
}
@Override
public MainHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.activity_main_item, parent, false);
MainHolder mainHolder = new MainHolder(binding.getRoot());
mainHolder.setBingDing(binding);
return mainHolder;
}
@Override
public void onBindViewHolder(MainHolder holder, int position) {
ViewDataBinding bingDing = holder.getBingDing();
bingDing.setVariable(BR.user,list.get(position));
bingDing.setVariable(BR.position,position);
bingDing.executePendingBindings();
}
@Override
public int getItemCount() {
return list.size();
}
public void setData(ArrayList<User> data) {
list.clear();
list.addAll(data);
}
public static class MainHolder extends RecyclerView.ViewHolder{
private ViewDataBinding bingDing;
public MainHolder(View itemView) {
super(itemView);
}
public ViewDataBinding getBingDing() {
return bingDing;
}
public void setBingDing(ViewDataBinding bingDing) {
this.bingDing = bingDing;
}
}
}
然后是activity的使用
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding viewDataBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
MainAdapter mainAdapter = new MainAdapter(this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL,false);
ArrayList<User> users = initData();
mainAdapter.setData(users);
viewDataBinding.rl.setLayoutManager(linearLayoutManager);
viewDataBinding.rl.setAdapter(mainAdapter);
User user = new User();
users.add(user);
mainAdapter.setData(users);
users.get(10).setFirstName("hahahahahah");
}
private ArrayList<User> initData() {
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 50; i++) {
User user = new User();
user.setFirstName("学生"+i+"号");
users.add(user);
}
return users;
}
}
我们发现代码是不是简洁了很多啊.
六,其他用法
下面这中情况可能我们会遇到,当我们布局中有个imageview的时候貌似databingding就失灵了,因为我们图片都是从网络加载的啊,别担心dataBingding为我们准备了一个东西来解决这类问题
比如布局如下:我们为ImageView 设置了这么一个属性 app:image="@{imageUrl}
这是怎们回事,请往下看
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class=".....User">
<variable
name="imageUrl"
type="String" />
</data>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:image="@{imageUrl}"/>
</layout>
我们需要定义这么一个方法,你可以专门搞个BingdingUtils 方此类的方法,
在这个方法中我们用到一个注解BindingAdapter,这个就是来自定义一个get方法,方法中我们具体实现这个属性的内容(从网络上加载一个图片设置给imageView),xml中用到的在定义属性app:image就是我们 @BindingAdapter({"bind:image"}) 定义的这个,这是一个数组,所以呢可以定义很多个属性来用这个方法,然后即使在xml中使用 app:image="@{imageUrl}"这样就可以把这个网络地址的图片设置给我们的ImageView ,当然你需要在java代码中set这个值.
public class BingdingUtils {
@BindingAdapter({"bind:image"})
public static void imageLoader(ImageView imageView, String url) {
ImageLoaderUtils.getInstance().displayImage(url, imageView);
}
}
还有一种情况比如我们要设置text属性,而拿到的值却不是一个string,或者说我们想在所有的text设置之前做一些操作,等等...这个属性是自带的我们没法去自定义一个BindingAdapter,这种情况要怎们做?
谷歌还为我们提供了一个东西叫做Converter,就是字面的意思转换器
就像下面的代码一样,我们创建一个转换器,用BindingConversion注解来标准,这个东西是统一性的,只要你用databingding设置的内容的类型是这个方法的参数类型,比如下面的这个方法就是String,呢设置的这个内容就会被转换,所以这个也要看好了再用,
不过这个一般可以解决很多问题.
public class MainConverter {
@BindingConversion
public static String convertDate(String string) {
return string+"fule ";
}
}
以上就是databing大大小小的一些用法,当然还需要在项目中去实操才能熟练使用.
网友评论
<variable
name="map"
type="HashMap<String, String>" />
这里的HashMap不能使用尖括号