阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解。

简介

我们知道,DataBinding的核心理念是数据驱动。数据驱动驱动的目标就是View,使用DataBinding,我们通过添加、修改、删除数据源,View就会自动予以相关变化。

Android RecyclerView的Adapter起的作用就是连接数据和View

一个最简单的RecyclerView Adapter可能是下面这个样子的:

public class UserAdapter extends RecyclerView.Adapter
{
@Override
public int getItemCount()
{
return 0;
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return null;
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{ }
}

通过getItemsCount(), RecyclerView知道了所有子项的数量。

通过onCreateViewHolder(), RecyclerView知道了每一个子项长什么样子。

通过onBindViewHolder(),让每个子项得以显示正确的数据。

可以看到,Adapter起的作用和DataBinding是非常类似的,使用DataBinding,可以使Adapter的编写显得更加简单。

DataBinding简单使用

接下来看一个简单的例子。这个例子创建了一个简单的列表,效果如下:

我们看看,使用DataBinding该如何实现它。

Model类:

public class User
{
private String name;
private int age; public User(String name, int age)
{
this.name = name;
this.age = age;
} public String getName()
{
return name;
} public void setName(String name)
{
this.name = name;
} public int getAge()
{
return age;
} public void setAge(int age)
{
this.age = age;
}
}

View xml

<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import type="cn.zmy.databindingadapter.model.User"/>
<variable name="model"
type="User"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginBottom="10dp"
android:background="@android:color/darker_gray"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{model.name}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(model.age)}"/>
</LinearLayout>
</layout>

Adapter

public class UserAdapter extends RecyclerView.Adapter
{
private Context context;
private List<User> items; public UserAdapter(Context context)
{
this.context = context;
this.items = new ArrayList<User>()
{{
add(new User("张三", 18));
add(new User("李四", 28));
add(new User("王五", 38));
}};
} @Override
public int getItemCount()
{
return this.items.size();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
ItemUserBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), R.layout.item_user, parent, false);
return new UserViewHolder(binding.getRoot());
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
ItemUserBinding binding = DataBindingUtil.getBinding(holder.itemView);
binding.setModel(this.items.get(position));
binding.executePendingBindings();
} static class UserViewHolder extends RecyclerView.ViewHolder
{
public UserViewHolder(View itemView)
{
super(itemView);
}
}
}

Activity

public class MainActivity extends AppCompatActivity
{ @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new UserAdapter(this));
}
}

可以看到,使用了DataBinding之后,我们在onBindViewHolder中,无需再写一些类似于holder.view.setXXX()的代码,因为这些在Xml中就已经完成了。

优化

上面的Adapter还能不能更简单呢?

优化ViewHolder

我们发现,Adapter中的UserViewHolder几乎没有做任何事。事实上,我们声明它完全是由于AdapteronCreateViewHolder需要这么一个返回值。

我们可以把ViewHolder提出来,这样所有Adapter都可以使用而无需在每个Adapter中都声明一个ViewHolder。

取名就叫BaseBindingViewHolder

public class BaseBindingViewHolder extends RecyclerView.ViewHolder
{
public BaseBindingViewHolder(View itemView)
{
super(itemView);
}
}

优化getItemCount

getItemCount返回了子项的数量。

由于几乎每个Adapter都会存在一个List用于保存所有子项的数据,我们完全可以创建一个Adapter基类,然后在基类中实现getItemCount

优化onCreateViewHolder&onBindViewHolder

onCreateViewHolder代码如下:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
ItemUserBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), R.layout.item_user, parent, false);
return new BaseBindingViewHolder(binding.getRoot());
}

可以看到,这个方法里面唯一的“变数”就是“R.layout.item_user”这个layout。

onBindViewHolder代码如下:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
ItemUserBinding binding = DataBindingUtil.getBinding(holder.itemView);
binding.setModel(this.items.get(position));
binding.executePendingBindings();
}

可以看到,这个方法先获取到View的Binding,然后给Binding的Data赋值。Binding从哪里来?都是通过DataBindingUtil.getBinding(holder.itemView)获取到的。

本着不写重复代码,能封装就封装的原则,我们来创建Adapter基类。代码如下:

public abstract class BaseBindingAdapter<M, B extends ViewDataBinding> extends RecyclerView.Adapter
{
protected Context context;
protected List<M> items; public BaseBindingAdapter(Context context)
{
this.context = context;
this.items = new ArrayList<>();
} @Override
public int getItemCount()
{
return this.items.size();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
B binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), this.getLayoutResId(viewType), parent, false);
return new BaseBindingViewHolder(binding.getRoot());
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
B binding = DataBindingUtil.getBinding(holder.itemView);
this.onBindItem(binding, this.items.get(position));
} protected abstract @LayoutRes int getLayoutResId(int viewType); protected abstract void onBindItem(B binding, M item);
}

然后使UserAdapter继承自上面封装的BaseBindingAdapter,代码如下:

public class UserAdapter extends BaseBindingAdapter<User, ItemUserBinding>
{
public UserAdapter(Context context)
{
super(context);
items.add(new User("张三", 18));
items.add(new User("李四", 28));
items.add(new User("王五", 38));
} @Override
protected int getLayoutResId(int viewType)
{
return R.layout.item_user;
} @Override
protected void onBindItem(ItemUserBinding binding, User user)
{
binding.setModel(user);
binding.executePendingBindings();
}
}

可以看到,优化后的Adapter除去初始化User数据源的那部分代码,实际上的核心代码就寥寥数行。

通过getLayoutResId我们告诉了RecyclerView子项长什么样子。

通过onBindItem我们给具体的每个子项绑定了合适的数据。

至于具体的绑定过程,是放在布局的xml文件中的。

优化数据源

我们的数据源是在构造函数中这样添加的:

items.add(new User("张三", 18));
items.add(new User("李四", 28));
items.add(new User("王五", 38));

在实际开发过程中,我们极少这么做。因为通常在构造Adapter的时候,我们并未得到任何有效的数据。数据源可能是通过网络请求从服务器得来,也可能是通过查询本地数据库表得来。我们在构造Adapter之后,可能还需要较长的时间去获取有效的数据源,这就要求必须在Adapter构造完成之后,外部调用者还可以修改的数据源。

我们可以这样做:

adapter.items.add(XXX);
adapter.notifyItemInserted();

这样我们新增数据源之后,adapter也知道我们修改了数据源,进而View也就能随之变化。

不过有了DataBinding,我们可以更为巧妙的实现上述操作。

ObservableArrayList

ObservableArrayList是Android DataBinding库中的一个类。

public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T>
{
...
}

ObservableArrayList实现了ObservableList接口。通过ObservableList,我们可以为ObservableArrayList添加一个或多个Listener。当ObservableArrayList中的数据发生变化时(添加了一个或多个元素、删除了其中某个或某些元素时),这些Listener或收到数据源发生改变的通知。

其实ObservableArrayList的实现并不复杂,只需要重写addaddAllremove等等等等这些可能造成集合发生变化的方法就可以实现上述效果。

虽然实现不复杂,但是ObservableArrayList却可以解决我们上面遇到的修改数据源的问题。

我们只需要在集合发生改变时,调用adapter.notifyXXX()等方法就可以实现当数据源发生变化时,View也可以自动发生变化,而外部却无需调用adapter.notifyXXX()了。

代码实现

我们再次修改BaseBindingAdapter的代码,使之支持数据源发生变化时,自动更新View。

public abstract class BaseBindingAdapter<M, B extends ViewDataBinding> extends RecyclerView.Adapter
{
protected Context context;
protected ObservableArrayList<M> items;
protected ListChangedCallback itemsChangeCallback; public BaseBindingAdapter(Context context)
{
this.context = context;
this.items = new ObservableArrayList<>();
this.itemsChangeCallback = new ListChangedCallback();
} public ObservableArrayList<M> getItems()
{
return items;
} @Override
public int getItemCount()
{
return this.items.size();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
B binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), this.getLayoutResId(viewType), parent, false);
return new BaseBindingViewHolder(binding.getRoot());
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
B binding = DataBindingUtil.getBinding(holder.itemView);
this.onBindItem(binding, this.items.get(position));
} @Override
public void onAttachedToRecyclerView(RecyclerView recyclerView)
{
super.onAttachedToRecyclerView(recyclerView);
this.items.addOnListChangedCallback(itemsChangeCallback);
} @Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView)
{
super.onDetachedFromRecyclerView(recyclerView);
this.items.removeOnListChangedCallback(itemsChangeCallback);
} //region 处理数据集变化
protected void onChanged(ObservableArrayList<M> newItems)
{
resetItems(newItems);
notifyDataSetChanged();
} protected void onItemRangeChanged(ObservableArrayList<M> newItems, int positionStart, int itemCount)
{
resetItems(newItems);
notifyItemRangeChanged(positionStart,itemCount);
} protected void onItemRangeInserted(ObservableArrayList<M> newItems, int positionStart, int itemCount)
{
resetItems(newItems);
notifyItemRangeInserted(positionStart,itemCount);
} protected void onItemRangeMoved(ObservableArrayList<M> newItems)
{
resetItems(newItems);
notifyDataSetChanged();
} protected void onItemRangeRemoved(ObservableArrayList<M> newItems, int positionStart, int itemCount)
{
resetItems(newItems);
notifyItemRangeRemoved(positionStart,itemCount);
} protected void resetItems(ObservableArrayList<M> newItems)
{
this.items = newItems;
}
//endregion protected abstract @LayoutRes int getLayoutResId(int viewType); protected abstract void onBindItem(B binding, M item); class ListChangedCallback extends ObservableArrayList.OnListChangedCallback<ObservableArrayList<M>>
{
@Override
public void onChanged(ObservableArrayList<M> newItems)
{
BaseBindingAdapter.this.onChanged(newItems);
} @Override
public void onItemRangeChanged(ObservableArrayList<M> newItems, int i, int i1)
{
BaseBindingAdapter.this.onItemRangeChanged(newItems,i,i1);
} @Override
public void onItemRangeInserted(ObservableArrayList<M> newItems, int i, int i1)
{
BaseBindingAdapter.this.onItemRangeInserted(newItems,i,i1);
} @Override
public void onItemRangeMoved(ObservableArrayList<M> newItems, int i, int i1, int i2)
{
BaseBindingAdapter.this.onItemRangeMoved(newItems);
} @Override
public void onItemRangeRemoved(ObservableArrayList<M> sender, int positionStart, int itemCount)
{
BaseBindingAdapter.this.onItemRangeRemoved(sender,positionStart,itemCount);
}
}
}

然后我们修改Activity的代码:

public class MainActivity extends AppCompatActivity
{ @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this)); UserAdapter adapter = new UserAdapter(this);
recyclerView.setAdapter(adapter); adapter.getItems().add(new User("张三", 18));
adapter.getItems().add(new User("李四", 28));
adapter.getItems().add(new User("王五", 38));
}
}

可以看到,外部仅仅将数据添加到了数据源中,而没有做任何其他操作。不过我们的View还是更新了,效果和上面是一样的。这也符合DataBinding的核心原则:数据驱动。使用DataBinding,我们关心的只有数据源,只要数据源发生改变,View就应随之发生改变。

Demo

文章中的代码已整理上传至Github。

链接:https://github.com/a3349384/DataBindingAdapter

博客:https://www.zhoumingyao.cn/

极简的Android RecyclerView Adapter(使用DataBinding)的更多相关文章

  1. Android RecyclerView.Adapter notifyDataSetChanged 不起作用

    我在自己动手写RecyclerView的上拉加载更多,最后就差一步,这个时候数据已经加载完了,UI上面没有显示,我而且也调用了notifyDataSetChanged刷新item的数据,但是一直没效果 ...

  2. Android RecyclerView Adapter 新式用法之SortedListAdapterCallback

    引言 前几天在同事的提醒下发现V7中有了一个新的工具类SortedListAdapterCallback,配合RecyclerView Adapter和SortedList一起使用更加方便的管理我们在 ...

  3. 极简Unity调用Android方法

    简介 之前写了篇unity和Android交互的教程,由于代码里面有些公司的代码,导致很多网友看不懂,并且确实有点小复杂,这里弄一个极简的版本 步骤 废话不多说,直接来步骤吧 1.创建工程,弄大概像这 ...

  4. Android开发教程 - 使用Data Binding(六)RecyclerView Adapter中的使用

    本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fr ...

  5. Android 利用RecyclerView.Adapter刷新列表中的单个view问题

    首先使用RecyclerView的adapter继承:RecyclerView.Adapter public class OrderListAdapter extends RecyclerView.A ...

  6. Android官方数据绑定框架DataBinding

    数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那么多的findViewById. ...

  7. Android RecyclerView与ListView比较

    RecyclerView 概述 RecyclerView 集成自 ViewGroup .RecyclerView是Android-support-V7版本中新增的一个Widgets,官方对于它的介绍是 ...

  8. Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

  9. Android RecyclerView 的简单使用

    Android L SDK发布的,新API中最有意思的就是RecyclerView (后面为RV) 和 CardView了, 按照官方的说法, RV 是一个ListView 的一个更高级更灵活的一个版 ...

随机推荐

  1. dedecms列表页调用子栏目列表,织梦首页调用栏目的子栏目标签代码

    dedecms列表页调用子栏目列表,织梦首页调用栏目的子栏目标签代码. dedecms列表页调用子栏目列表标签: {dede:channelartlist type='sun' }<a href ...

  2. xssless - 自动化的XSS payload攻击器

    XSSLESS 一个用Python编写的自动化XSS 负载(payload)攻击器 用法: 记录请求 并结合Burp proxy 选择你想生成的请求,然后右键选择“保存项目” 使用xssless生成你 ...

  3. Spring学习(4)---Bean基础

    Bean配置项 Bean的作用域 Bean的生命周期 Bean的自动装配 Resources & ResourceLoader (一) Bean配置项 常用的配置项 Id   (IOC容器中B ...

  4. Shiro眼皮下玩ajax,玩出302 Found(实践得经验)

    2017/06/14这一天,是我玩Shiro安全框架最刻骨铭心的一天.因为Shiro今天给我深深的补了一刀,在这儿我也给各位补一刀吧,其实问题很简单,解决方式也极其简单,只是给各位分享一下这个错误,纯 ...

  5. node.js零基础详细教程(6):mongodb数据库操作

    第六章 建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  6. 理解Java中的抽象

    在计算机科学中,抽象是一种过程,在这个过程中,数据和程序定义的形式与代表的内涵语言相似,同时隐藏了实现细节. 抽象:一个概念或者想法不和任何特定的具体实例绑死. 目录 什么是抽象 抽象的形式 如何在J ...

  7. ubuntu 系统 更改屏幕亮度为最大(15级亮度)

    历经千辛万苦终于搞定屏幕亮度,现将成果分享如下. 硬件:联想K29 系统:UBUNTU 14.04 一.执行命令 sudo gedit /etc/default/grub 二.更改文本 然后找到 GR ...

  8. ios在Xcode里关于图片的权限设置

    <key>NSPhotoLibraryUsageDescription</key> <string>This app requires access to the ...

  9. 轻松Angularjs实现表格按指定列排序

    angular表格点击序号进行升序,再次点击进行降序排序,在输入框输入信息,出现相对应数据的那一行. html: <input type="text" ng-model=&q ...

  10. css3实现可以计算的自适应布局——calc()

    开始我们需要先了解什么是calc() ,calc()是一个CSS函数,你可以使用calc()给元素的margin.pading.width等属性设置 而且你还可以在一个calc()内部嵌套另一个cal ...