DataBinding 的简单使用

先吐槽下,不说不爽,不说不通达

不吐不快,集合我这几天学习 DataBinding 的经历说几句。DataBinding 这东西也不是 android 的专利,android 引入这个功能是后知后觉的,一步一趋的跟着别的系统脚步发展的。纵观全局,14-16年是 android 技术大爆发的年头,各种新技术层出不穷,目不暇接,到17年中呢android 技术的进步就停下来了,android 开发呢也是进入生命周期内最辉煌的时候,技术已经非常成熟了,可以遇见的未来,android 的技术短期内没有什么大的进步,改变了。作为一个普通的 android 开发者,我们要在这个时间点上努力的吸收之前几年 android 开发技术的精髓,和各种优秀的思路,思想,手段和套路,我觉得这就是我们做 android 开发应该详细了解,举一反三的,这些也移动开发的核心精髓,换个平台,换个系统,除了基础的开发语言和构建工具,和系统知识体系的变化外,剩下的都是要重复或者再走 android 这些年这些技术发展的老路,我认为相同领域技术思路都是趋同的,区别是不同平台,不同语言的具体实现罢了。


什么是 DataBinding

什么是 DataBinding 呢,简单说来就是帮我们实现 view 和 data 绑定的工具,把数据映射到 view 的 xml中,可以在 xml 布局文件中实现 view 的赋值,方法调用。使用 DataBinding 后,我们不同再写 findViewById,不用再获取控件对象,不用再设置监听,可以节省我们 activity 中的很多获取控件,赋值,添加监听所需要的代码。

DataBinding 是个好东西啊,15年 google IO 大会就开始推了,最直接的变化就是催生了 android 中 MVVM 的出现,MVVM = MVP + DataBinding 。一线公司早就普及 MVVM 了,大伙作为一个普普通通的 andoid 开发者,一定要身上时代,什么是时代,大厂就是时代,大厂都在干什么,我们就干什么,不求跟上大厂,但求不落后太多,所以小伙伴们走起,MVVM 作为 MVP 的进阶,我们一定要学好,这期中的重中之重 DataBinding 一定不要落下,其实没多难,DataBinding 初步学习半天就可以,其中涉及到列表和多module的部分是比较复杂 的,我们多看几个开源的 app 例子就行,这里我也尽量详细的说一说。经验是干什么的,就是带着我们少走弯路,快速学习的,有的可以快,有的不能快,必须去体会,恰巧 DataBinding 就是可以快起来的部分。


DataBinding 的初步使用

写了一段时间的博客后,我是深深体会到了,一不论多复杂的事一定要按步骤拆解成一段段简单的步奏,这样才能学的快,学的明白,中间断掉了,之后也好捡起来继续学。

千里之行,始于足下,所以呢,我们先来把 DataBinding 集成进来,做个最简单的实现。

先导入 DataBinding 功能

因为 DataBinding 是 google 强推的嘛,所以 1.5版以上 gradle 就自带 DataBinding 库了,只要我们在 gradle 配置中打开 DataBinding 这个功能就可以了,不用再引入远程依赖了。哪个 module 需要就在哪个 module 的 build.gradle 编译配置文件中声明启动 dataBinding 就可以了。注意 Gradle 最低要 1.5 alpha 版。

android {
    ...
    dataBinding {
        enabled = true
    }
    ...
}

这里我在 app 这个 module 里面声明的。DataBinding 在 gradle 中的表现形式就是 DataBinding 相关的 task 编译任务了,我们 enabled = true 之后,在编译时就会执行 DataBinding 的 task 了。

在 xml 中使用
 

    
    
        
        
    

    

        

        

    

注意 dataBinding 在 xml 中的使用规则:

  • 使用 DataBinding 必须要使用 这个标签作为 xml 的根部局
  • 所有 DataBinding 的内容都写在 标签内
  • 是导入所使用的对象的类型
  • 是具体声明一个数据对象, 是这个对象的名字, 是这个对象的类型, 可以有多个
  • 使用 @{ } / @{book.price} 的方式可以把这个声明出来的数据对象的莫个数据设置给我们的 view
java 代码的使用部分
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        Book book = new Book("AAA", "35");
        mainBinding.setBook(book);
    }
}
  • 首先我们使用 DataBinding 的方式替换我们的 setContentView,使用 DataBindingUtil 这个类的 setContentView 方法,里面传入activity 和 layout 布局
  • 然后 DataBinding 的 setContentView 方法会根据类名给我们返回一个 DataBinding 的辅助类,这里就是这个 ActivityMainBinding 了。名字规则是 Activity 类型名在前 + 类中其他的名字,这个 ActivityMainBinding 辅助类我们不用编译 AS 也会自动帮我们生成,但是呢要是你在这里有问题,那就手动编译一下。
  • 我们拿到辅助类之后,new 一个数据对象出来,然后把这个数据对象设置给辅助类 ActivityMainBinding,因为我们在 xml 中的中声明几个数据对象,那我们在 activity onCreate 中就得给 DataBinding 的辅助类 ActivityMainBinding,设置几个数据对象进去,然后 DataBinding 才可以根据我们设置进来的数据对象,给相关 view 设值。

DataBinding 的数据更新

view 在本质的职责是反应数据的变化,那么我们就来说说在 DataBinding 中我们怎么更新数据。DataBinding 的数据本质是我饿们在 xml 中 标签中声明的数据对象,我们在java 代码中把相关的数据对象设置给 DataBinding 的辅助类,那么我们有一下几种方式:

  • 整体再设置一次数据 bean 对象,这个适合更新整体数据
  • 使用 BaseObservable ,操作数据 bean 的 set 方法更新字段数据,适合更新部分数据
  • 使用 ObservableFields ,只更新有需求的数据字段,这个不够灵活
  • 使用 DataBinding 也有的集合类型:ObservableArrayMap , ObservableArrayList
  • 双向数据绑定
更新整体数据

这个本质上就是重复一次我们给 DataBinding 辅助类设置数据的过程,使用很简单

 Book book = new Book("BBBB", "65");
 mainBinding.setBook(book);

这样就可以了,说实话,我更倾向于这样方式,因为大多数时候,数据都是休要整体更新的

使用 BaseObservable

BaseObservable 是一个基类,需要我们的数据 bean 继承这个基类,然后给属性的 get 方法
添加@Bindable 这个注解,然后在属性的 set 方法中添加上 DataBinding 更新某个字段的方法

public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    } 

这样做呢,其实是在 DataBinding 的辅助类中把相关属性的更新和 view 的赋值方法关联在一起,完整的数据 bean 如下:

public class Book extends BaseObservable {

    public String name;
    public String price;

    public Book() {
    }

    public Book(String name, String price) {
        this.name = name;
        this.price = price;
    }

    @Bindable
    public String getName() {
        return name;
    }

   public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
        notifyPropertyChanged(BR.price);
    }
}
在 activity 中操作数据 bean 的 set 方法就可以同步把数据更新到 view 中了
mBook.setName("CCC");

有个更好的解释:

Obserable接口有一个自动添加和移除监听器的机制,但是通知数据更新取决于开发者。为了使开发变得简单,谷歌创建了BaseObserable这个基础类来集成监听器注册机制。通过给getter方法添加Bindable注解,通知setter方法。

使用 ObservableFields

ObservableFields 是一个对属性添加 DataBinding 更新功能的代理类,针对不同的数据类型有不同类型的 ObservableFields :ObservableBoolean、 ObservableByte ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、 ObservableParcelable 等。

这种方式不是主流类型,使用不便,不能扩展属性,所以这里我简单的放个例子:

// 数据 bean 中声明一个属性
 public ObservableField age = new ObservableField<>(); 

// activity 中更新数据
 mBook.age.set("ABABA");
ObservableArrayMap , ObservableArrayList

这2个集合类型是 DataBinding 为了方便数据刷新提供的,不用我们再手动通知集合的变化,只要我们更新了集合的某些数据,就能自动更新相关的 view 的数据

简单举个例子:

// 声明一个集合对象
private ObservableArrayMap mapUser = new ObservableArrayMap<>();

// 这样直接更新集合数据就可以了
mapUser.put("firstName", "zhu");
mapUser.put("lastName", "chen");
mapUser.put("age", 27);
双向数据绑定

双向数据绑定就是当 view 改变时,data 会跟着改变;data 改变时,view 会跟着改变,核心就是给 view 指定 :@={book.name},这里用 EditText 做个例子

  

这样当输入不同的内容时,数据就会同步更新到绑定的 book 这个数据对象中。


DataBinding 的更丰富使用

上文书书说 DataBinding 的精髓都在布局的 xml 文件中,其中有着丰富的操作,DataBinding 在xml 有越多的用法,那么就会越多的替代我们 java 中的代码,越多的减少 activity 的代码量,而且使用 DataBinding 后,页面的赋值逻辑在 xml 也会显得刚加清晰,可见。那么现在我们就来看看 DataBinding 都有那些玩法。

1. variable 标签支持的数据类型

java 能使用的 variable 标签当然都能

  • 基本数据类型,我们按照 java 的写法即可,比如 String,int
  • 若是引入了名字相同的类,可以给类添加别名
  • 引用类型使用 import 引入全包名即可
  • list,map 结合类型同引用类型 import 导下包名就行
// 引用类型



android:text="@{book.name}"

------------------------------------

// 基本数据类型
// 基本数据类型不用 import 导入,直接在 type 类型里写就行qw,写 int 可能会报错,忽略就行,写相应的包装类型就得引入包了



// 在使用 int 等基本数据类型时,注意转成字符串再赋值,DataBinding 不会帮我们做类型转换的
android:text="@{String.valueOf(price)}"

------------------------------------

// 集合数据类型
// 集合的使用方式包括 [] 和 get 2种方式





android:text="@{bookList.get(1).name}"  
android:text="@{bookList[1].price}"  
android:text='@{bookMap["111"].name}'
android:text='@{bookMap.get("111").price}'
// 注意其中特殊特好的使用,集合枚举的 <> 符号直接写xml 不认,需要用转移符号,写 map 时,key 要是 String 的,那么你可以再里面用 " " ,但是这行的 xml 外面就得用 ' ' 才行,这点注意啊,要不 xml 总是报错

------------------------------------

// 设置别名
// 我们引入的不同包的类可能重名,那么自然我们需要加一个别名来加以区分了,用alias表示





常用的转义字符表

2. 如何调用,注册各种方法

大家想啊,既然在 xml 中都可以直接操作属性值了,那么我调用一个方法还不是妥妥的啊。这里要区分方法的调用和监听方法的注册

对于一个 button 的点击事件来说,我们可以走下面2中方式:

  • DataBinding 会根据 id 名,生成相应的 view 对象,然后我们给这个 view 设置监听
 mainBinding.btnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText( MainActivity.this,"AAA",Toast.LENGTH_SHORT ).show();
            }
        });
  • 在 xml 中声明一个点击事件的对象,然后设置进 android:onClick 属性里
// 先声明点击事件对象
 
 

// 再使用
 

总体感觉和原来的注册方式没啥区别

除了各种事件的注册外,我们在利用 DataBinding 可以在 XML 中使用各种方法,静态方法,和对象中的方法都是可以直接调用的,注意是在给 view 属性赋值时可以直接调用

// Utils 里面有一个静态方法

// 导入包含静态方法的类,DataBinding 中要想使用任何类型,除了基本数据类型,都得导包
 



// 方法直接使用即可,直接传参也是可以的
 
3. DataBinding 对 lambda 表达式的支持

DataBinding 支持我们直接在 xml 书写 lambda 表达式,常用的就是注册 click 点击事件了,在 view 的 onClick 属性中我们需要传入一个对象,通过上面的内容学习,我们是声明了一个 ClickListener 类型的对象数据出来,然后在 java 中传入这个对象的方式做的。但是我们在这里可以直接实现 lambda 表达式书写一个匿名实现类对象出来,这样就省了我们在 java 中传入对象的代码了。这里说一下 lambda 表达式就是对匿名实现类对象的简写,所以我们虽然看着像是调用了方法的样子,其实我们是写了一个匿名实现类对象出来,本质上还是传入了一个对象。不熟悉 lambda 表达式的看这里:Lambda表达式以及AS 对其的支持 ,还是推荐大家去学习一下 Lambda的,现在各大语言都在往这种函数式编程上靠,像 lambda 表达式这种从函数式编程上借鉴过来的东西,以后只会越来越多的,抗拒这种变化是不明智的。

大家可能会这么写 Lambda 表达式

 

但是很遗憾,你这么写会报错,一个类型转换异常的错误,知道为什么吗?这个还是要从 Lambda表达式说起。Lambda 的特征是隐藏实现类的 类名 和 方法名,因为 java 8可以知道从上下文(方法中对于参数的限定)知道类的类型,并且规定了类的里面只能有一个方法,那么这里我们使用的就是这个方法:

 public void onClick(View v) {
                ......
           }

注意我们使用 Lambda 重写的就是这个 onClick 方法,onClick 方法要求传入一个 view 的参数的,上面我们没有传这个参数,所以报错了。那么我们把 Lambda 表达式修改一下:

 

view 表示点击事件的 view 对象,这样 Lambda 就可以跑了。不过呢,使用 Lambda 写点击事件对象是属于动态类型的,因为每点击一次都会从心生成一个 点击的 匿名实现类 设置 view,所以注意这个动态的特性,合理利用,因为这样点击事件的数据也是可以使用新的了。

Class Listener Setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

4. 其他一些监听器
除了 onClick 之外,还提供了一些特定的点击事件,这里需要注意,下面几个我没用到过,也是从别人那里摘过来的:

Class Listener Setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

这里不知道为啥出现2个相同的表格出来,郁闷啊

5. 支持表达式语言

表达式语言和java语法很相似。以下是相同的:

  • Mathematical: + - / * %
  • String concatenation +
  • Logical && ||
  • Binary & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Comparison == > < >= <=
  • instanceof
  • Grouping ()
  • Literals - character, String, numeric, null
  • Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

6. 不支持的操作

java中的一些特性在DataBinding语法中不支持

  • this
  • super
  • new
  • Explicit generic invocation

7. Null Coalescing 空运算符

android:text="@{user.displayName ?? user.lastName}"

就等价于

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

8. 关于字符串的符号再次说明一下

可以在属性值使用单引号,在表达式中字符串的值使用双引号:

android:text='@{map["firstName"]}'

也可以在属性值使用双引号,表达式中的值字符串的值应该使用单引号或者是"`"。

android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"

9. 自定义 DataBinding 辅助类类名

默认情况下,binding 类的名称取决于布局文件的命名,以大写字母开头,移除下划线,后续字母大写并追加 “Binding” 结尾。这个类会被放置在 databinding 包中。举个例子,布局文件 contact_item.xml 会生成 ContactItemBinding 类。如果 module 包名为 com.example.my.app ,binding 类会被放在 com.example.my.app.databinding 中。

通过修改 data 标签中的 class 属性,可以修改 Binding 类的命名与位置。


    ...

以上会在 databinding 包中生成名为 CustomBinding 的 binding 类。如果需要放置在不同的包下,可以在前面加 “.”


    ...

这样的话, CustomBinding 会直接生成在 module 包下。如果提供完整的包名,binding 类可以放置在任何包名中


    ...

10. DataBinding 中使用资源文件

资源 id 方面我们同传统方式去写就行,但是还是稍有些差别的。不如看这个例子:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

运行起来,可能有的版本会报错误,因为这是 DataBinding 的 bug,有人说修了,有人说还有,碰上的朋友这样改

android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"

因为 DataBinding 生成数据的数据格式可能和我们实际需要的不同,注意这点。

其他的例子我们举一个,替换 String.xml 中的占位符:

// 先声明字符串资源
Full Name: %1$s:%2$s

// DataBinding 在 xml 中可以直接引用使用
android:text="@{@string/nameFormat(firstName, lastName)}"

DataBinding 对于资源的文件头命名可能和 android 传统的有些地方不一样,下面的表是官方文档上的,找资料的话都是这张表,没有其他的资料,大家实际要到问题可以参考这样表,但是优先还是按照 android 原先的资源引用方式来:


DataBinding 的简单使用_第1张图片
这几天写列表有问题,截图顶一下

11. DataBinding 中使用资源文件


DataBinding 使用技巧

1. 对 include 的支持

xml 布局中不可避免的要使用 include 标签,那么 DataBinding 怎么兼容这个 include 标签呢。其实只要 外层的 xml 把 include 声明的 DataBinding 数据对象传给 include 就行,但是要注意 DataBinding 不支持 include 的 merge 标签

include 的布局 name.xml




    
        
    

    

        
    


总体的布局文件



    

        
        
        
        
    

    

        

        

        
    

另一个 include 的 xml ,layout_input.xml




    

        
    


注意啊,如何调 include 布局的某一个组件呢

  binding.layoutInput.etName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                User user = new User(s.toString(), "Liang");

                binding.setUser(user);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

2. 对 fragment 的支持

上面我们演示的 activity,是通过 DataBinding 通过 setContentView 方法实现 DataBinding 和 activity xml 布局实现绑定的,那么问题来了 fragment 怎么办

activity 我们这么写

private ActivityDemoBinding mBinding;
protected void onCreate(Bundle savedInstanceState) {
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
 }

Fragment 我们这么写:

private FragmentHomeBinding mBinding;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    mBinding = DataBindingUtil.inflate(inflater, R.layout.homepage_fragment, container, false);
    return mBinding.getRoot();
}

3. 对 ViewStub 的支持

// 我们给 ViewStub 设置初始化函数
 mainBinding.bookStub.setOnInflateListener(new ViewStub.OnInflateListener() {
            @Override
            public void onInflate(ViewStub stub, View inflated) {
                bookStubBinding = DataBindingUtil.bind(inflated);
                bookStubBinding.setBook(mBook);
            }
        });

// 模拟一个点击事件加载 ViewStub 
 mainBinding.setTestClick(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mainBinding.bookStub.isInflated()) {
                    mainBinding.bookStub.getViewStub().inflate();
                } else {
                    mBook.setName("stub");
                    mBook.setPrice("888");
                }
            }
        });

4. DataBinding 中的注解

DataBinding 可以使用的注解:

  • @BindingMethod :修改view 某个方法名字
  • @BindingAdapter : 给 view 添加set/get 方法,相当于添加自定义属性
  • @BindingConversion : 提供数据类型转换方法

@BindingMethod 用的不多,这里就不说了,我们来看下 @BindingAdapter 这个注解,可以给 view 添加自定义属性,相当的好用啊,我们不用再定义复杂的 view 自定义属性了,@BindingMethod 书写简单,功能强大,xml 中的自顶提示很友好。

先定义给 view 设置这个自定义属性的具体执行方法

public class TextBingUtils {

    @BindingAdapter("info")
    public static void setInfo(TextView view, String info) {
        view.setText(info);
    }

}

注意 @BindingAdapter 注解里面的参数就是这个自定义属性的名字,在 xml 里 app:xx 就可以使用了。另外这个方法写在哪里都没事,但是方法必须是静态的,DataBinding 框架在编译时会自动检索 @BindingAdapter 这个注解的所有方法。

然后在就可以在 xml 中使用了

 

@BindingConversion 用的不多,但是也得说一下,这个注解会给 DataBinding 提供默认的2个数据类型之间的转换方法,可以放置一些类型转换错误,写法上和 @BindingAdapter 相同。但是使用这转换器属性时我们必须要小心,因为DataBinding是不懂得区分是否真的需要使用整个转换器的。比方说我们创建两个相同类型的转换方法,DataBinding 就只会使用第一个

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
    return new ColorDrawable(color);
}


DataBinding 对 RecyclerView 的优化

为啥要单开一章说呢,因为的确是太重要了,DataBinding 对 RecyclerView 支持真的是一大亮点啊,直接的就是使用 DataBinding ,我们就不用再写 Viewholder 了。Viewholder 的工作就是 findviewbyid 持有相应的 view 的引用好让我们来设置数据,DataBinding 会自动根据 view 的 id 生成相关的对象,至少从这点出来,DataBinding 对我们都是非常有意义的了。好了,来看看在 RecyclerView 中使用 DataBinding 的基础方式。

新的 ViewHolder 书写方式,每个 ViewHolder 只需要持有相应 view 对应的 DataBinding 辅助类对象,通过他恩那个找到所有的子 view 引用。

public class BookBindingViewHolder extends RecyclerView.ViewHolder {

    private ItemListBinding t;

    public BookBindingViewHolder(ItemListBinding t) {
        super(t.getRoot());
        this.t = t;
    }

    public ItemListBinding getBinding() {
        return t;
    }

    public void setT(ItemListBinding t) {
        this.t = t;
    }
}

item 的布局文件,要相生成 DataBinding 辅助类,必须在 xml 中显示使用 DataBinding




    
        
        
    

    

新的 adapter 通过 DataBinding 类刷新数据

public class BookBindingAdapter extends RecyclerView.Adapter {

    private List data;

    public BookBindingAdapter(List data) {
        this.data = data;
    }

    @Override
    public BookBindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
    }

    @Override
    public void onBindViewHolder(BookBindingViewHolder holder, int position) {
        Log.d("AAA", "position:" + position);
        holder.getBinding().setBook(data.get(position));
        holder.getBinding().setVariable(BR.book, data.get(position));
        holder.getBinding().executePendingBindings();
    }

    @Override
    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    public void setData(List data) {
        this.data = data;
    }
}

holder.getBinding().executePendingBindings() 这句话是刷新界面,否则可能会出现这个问题: RecyclerView使用databinding出现数据闪烁问题

另外注意列表在更新数据时也可以使用 holder.getBinding().setVariable(BR.book, data.get(position)); 这个方法是更新xml 里面定义的数据对象的,BR.book 是个 int 值,指向xml 中声明的数据对象的id地址

当然上面这是最简单的 DataBinding 列表实现,甚至多 itemType 都不支持,下面优化下多 itemType 的问题

// 写一个返回 itemType 的接口,然后数据 bean 实现这个接口

public interface IBaseBindingAdapterItemProvide {
    int getItemType();
}

// 然后处理一下 adapter ,能够兼容多类型的 item

public class CatBindingAdapter extends RecyclerView.Adapter {

    private List data;

    public CatBindingAdapter(List data) {
        this.data = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == R.layout.item_list) {
            return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
        }
        if (viewType == R.layout.item_cat) {
            return new CatBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_cat, parent, false));
        }
        return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        if (holder instanceof BookBindingViewHolder) {
            Book book = (Book) data.get(position);
            ((BookBindingViewHolder) holder).getBinding().setBook(book);
            ((BookBindingViewHolder) holder).getBinding().executePendingBindings();
            return;
        }

        if (holder instanceof CatBindingViewHolder) {
            Cat cat = (Cat) data.get(position);
            ((CatBindingViewHolder) holder).getBinding().setCat(cat);
            ((CatBindingViewHolder) holder).getBinding().executePendingBindings();
        }
    }

    @Override
    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    @Override
    public int getItemViewType(int position) {
        return data.get(position).getItemType();
    }

    public void setData(List data) {
        this.data = data;
    }
}

恩,现在可以支持多类型的列表了,但是要是我们在实际开发中对这段原始代码不经任何雕琢(封装,设计),那么说明我们做开发真的是没长进。能够初步封装根据效果可以认为是初中级水平,能够写封装出一个易于扩展的优秀的库出来,可以视为高级水平了。期望大家多多用心,在封装自己的库时,是水平提升最快的时候了,知识点的学习是偏记忆,理解,考研智商和记忆能力。那么封装原始代码为库就是考研我们的基础代码水平了,设计到的都是硬知识点,也是最难以提升的部分,需要大毅力才行,但也是最重要的代码技能了。

本文先于篇幅,对于用 DataBinding 来优化 RecyclerView 就写到这里了,更多更优秀的内容我会开单章的,放到通过 MVP 学习代码封装的那部分里。


最后

  1. 不要拒绝 findViewById
    DataBinding 和 findViewById() 并不是互斥的,DataBinding的源码里面也是用到了findViewById()。如果某些情况真的不适合使用DataBinding,那就用回findViewById吧。

  2. xml中的表达式尽量简单
    xml 文件中不要出现过于复杂业务逻辑,只出现简单的 UI 相关的表达式。不要以为Data Binding是万能的,而想尽办法把逻辑写在xml中。往往这个时候你就应该将逻辑写在绑定的ViewModel里面。

  3. 注意clean
    有时候因为修改接口等原因要修改绑定的bean类,这时候偶尔会遇到一些神奇的bug。不要慌张,直接clean一下项目再make project一次往往就好了

  4. 使用BindingConversion注解时需要慎重
    原因上面已经说了。但是它也不失为一个很好用的注解,比方说使用它来进行字体的自定义。具体可以参照下面的文章:使用DataBinding来进行字体的自定义


参考资料

  • DataBinding系列(一):DataBinding初认识
  • DataBinding系列(二):DataBinding的基本用法
  • DataBinding系列(三):RecyclerView中使用DataBinding
  • DataBinding系列(四):DataBinding进阶之路
  • DataBinding的使用心得
  • DataBinding(一)-初识
  • DataBinding(二)-实战
    这里面说了一些 DataBinding 常用的方法,用到了看一下
  • 通过Data Binding为RecyclerView打造通用Adapter4

这里有一个 DataBinding 的使用规范的文章,不是赞同所有内容,但还是推荐大家看看

  • DataBinding的高手进阶实用指南

你可能感兴趣的:(DataBinding 的简单使用)