Android 数据绑定之: RecyclerView

本文讲的是Android 数据绑定之: RecyclerView,

简化, 复用, 重新绑定

有时我会想,“数据绑定”这个名词并不一定特指 Android 中的数据绑定。RecyclerView 就有它独特的方法将其数据绑定到 UI 控件上。它有一个 Adapter,其中需要我们实现两个非常重要的方法来进行数据绑定:

RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType);

void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

RecyclerView 对外暴露出了常见的 ViewHolder 模式 作为其 API 中的第一类公民。在 onCreateViewHolder() 方法中,View 被创建之后,其引用就被包含在 ViewHolder 中以便能被快速配置数据。然后在 onBindView() 方法中,特定的数据即和 View 关联起来。

RecyclerView 中的 Android 数据绑定####

前篇文章中指出 , Android 数据绑定可以被看作 ViewHolder 模式。理论上,我们只需在 onCreateViewHolder() 方法中返回生成的绑定类,但是这个类并没有继承 RecyclerView.ViewHolder 类。因此,这个绑定类必须被 ViewHolder ”包含“进去。

public class MyViewHolder extends RecyclerView.ViewHolder {
    private final ItemBinding binding;

    public MyViewHolder(ItemBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }

    public void bind(Item item) {
        binding.setItem(item);
        binding.executePendingBindings();
    }
}

现在,我们的适配器可以使用 Android 数据绑定的方法来创建并绑定数据:

public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                       int viewType) {
    LayoutInflater layoutInflater =
        LayoutInflater.from(parent.getContext());
    ItemBinding itemBinding =
        ItemBinding.inflate(layoutInflater, parent, false);
    return new MyViewHolder(itemBinding);
}

public void onBindViewHolder(MyViewHolder holder, int position) {
    Item item = getItemForPosition(position);
    holder.bind(item);
}

如果你看得够仔细的话,你可能会看到 MyViewHolder.bind() 方法最后有一个 executePendingBindings() 方法。这会强制绑定操作马上执行,而不是推迟到下一帧刷新时。RecyclerView 会在 onBindViewHolder 之后立即测量 View。如果因为绑定推迟到下一帧绘制时导致错误的数据被绑定到 View 中, View 会被不正确地测量,因此这个 executePendingBindings() 方法非常重要!

复用 ViewHolder

如果你之前曾经使用过 RecyclerView 的 ViewHolder,你会知道我们已经减少了一大堆关于将数据设置到 View 中的代码。但不幸的是,我们仍不得不为不同的 RecyclerView 写一大堆 ViewHolder。另外,如果你有多种 View 类型,你也不清楚如何拓展它。我们可以修复此问题。

通常来说,只有一个数据被传入到绑定类中,例如上文中的 "Item"。当你使用这种模式时,你可以使用类型转换来为各种 RecyclerView 以及各种 View 类型都使用唯一的 ViewHolder。按照惯例我们将单一视图模型对象命名成 "obj"。你也许会命名为 "item" 或者 "data",但如果我使用 "obj",将很容易在例子中辨别。

public class MyViewHolder extends RecyclerView.ViewHolder {
    private final ViewDataBinding binding;

    public MyViewHolder(ViewDataBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }

    public void bind(Object obj) {
        binding.setVariable(BR.obj, obj);
        binding.executePendingBindings();
    }
}

在 MyViewHolder 中,我使用了 ViewDataBinding,它是所有生成的绑定类的基类,代替了特定的 ItemBinding 类。这样之后,我就能在 ViewHolder 中支持各种各样的布局。我还使用了 setVariable() 方法来取代之前的类型安全,但需要指定特定类型的 setObj() 方式,这样我就能随意指定任何我需要的数据类型了。关键的一点是变量必须命名成 "obj" 因为我使用 BR.obj 作为 setVariable() 的键值。这意味着你必须在你的布局文件中有一个像这样的变量标签:

<variable name="obj" type="Item"/>

当然,你的变量相比于 "Item" ,能使用任何在布局中想要绑定的类型

之后我就能创建一个通用的 RecyclerView 适配器了。

public abstract class MyBaseAdapter
                extends RecyclerView.Adapter<MyViewHolder> {
    public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
        LayoutInflater layoutInflater =
                LayoutInflater.from(parent.getContext());
        ViewDataBinding binding = DataBindingUtil.inflate(
                layoutInflater, viewType, parent, false);
        return new MyViewHolder(binding);
    }

    public void onBindViewHolder(MyViewHolder holder,
                                 int position) {
        Object obj = getObjForPosition(position);
        holder.bind(obj);
    }

    @Override
    public int getItemViewType(int position) {
        return getLayoutIdForPosition(position);
    }

    protected abstract Object getObjForPosition(int position);

    protected abstract int getLayoutIdForPosition(int position);
}

在这个适配器中,布局的 ID 被用作 view 类型,这样能更方便得来获取正确的绑定类,同时也能让适配器处理任意数量的布局。但最通用的做法是 RecyclerView 只有一个布局,因此我们可以写这样一个基类:

public abstract class SingleLayoutAdapter extends MyBaseAdapter {
    private final int layoutId;

    public SingleLayoutAdapter(int layoutId) {
        this.layoutId = layoutId;
    }

    @Override
    protected int getLayoutIdForPosition(int position) {
        return layoutId;
    }
}

还剩下什么?

所有 RecyclerView 中的模板现在都被处理完了,留给你做的是最困难的部分:在非 UI 线程加载数据,当数据更新时通知适配器等等。Android 数据绑定仅简化了无聊的部分。

你也可以扩展这个技术来支持多个变量。通常来说需要支持一个事件处理对象来处理例如点击事件等,你也许会想将其传入到视图模型类中。如果你经常在 Activity 或 Fragment 中传值,你可以添加这些变量。只要你使用连贯的命名,你就可以在所有 RecyclerView 中使用这项技术。

使用 Android 数据绑定结合 RecyclerView 是简便的,能显著减少冗长的代码。也许你的应用只需要一个 ViewHolder 并且你再也不需要重写 onCreateViewHolder() 方法和 onBindViewHolder() 了!






原文发布时间为:2017年1月16日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2025-01-29 22:29:41

Android 数据绑定之: RecyclerView的相关文章

Android 使用自定义RecyclerView控件实现Gallery效果

上篇文章给大家介绍了Android 自定义 HorizontalScrollView 打造多图片OOM 的横向滑动效果.其实制作横向滚动的不得不说另一个控件,就是Google官方最近新增加的RecyclerView,据说是ListView的升级版本,本篇文章,首先介绍RecyclerView的用法,然后经行一定的分析:最后自定义一下RecyclerView实现我们需要的相册效果. 1.RecyclerView的基本用法 首先主Activity的布局文件: <RelativeLayout xmln

Android中使用RecyclerView实现下拉刷新和上拉加载_Android

推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

Android中使用RecyclerView实现下拉刷新和上拉加载

推荐阅读:使用RecyclerView添加Header和Footer的方法 RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterView,RecyclerView的刷新同样也是需

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

上篇给大家介绍了Android一步步带你在RecyclerView上面实现"拖放"和"滑动删除"功能 效果如下: 拖动手柄 在设计一个支持"拖放"的列表时, 通常提供一个在触摸时初始化拖拽的"拖动手柄". 因其可发现性和可用性而被Material Guidelines所推荐, 尤其是列表处于"可编辑模式"时. 首先更新item的布局(item_main.xml): <FrameLayout xmlns

Android开发中RecyclerView模仿探探左右滑动布局功能

我在此基础上优化了部分代码, 添加了滑动回调, 可自定义性更强. 并且添加了点击按钮左右滑动的功能. 据说无图都不敢发文章了. 看图: 1:这种功能, 首先需要自己管理布局 继承 RecyclerView.LayoutManager , 显示自己管理布局, 比如最多显示4个view, 并且都是居中显示. 底部的View还需要进行缩放,平移操作. public class OverLayCardLayoutManager extends RecyclerView.LayoutManager { p

Android 中使用RecyclerView实现底部翻页

RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.接下来通过一系列的文章讲解如何使用RecyclerView,彻底抛弃ListView. 最近在做pad端的app,需要一个像网页一样效果,之前使用addView方式,页码少的时候还可以,能实现效果,但是碰到了一个1000多页的界面,就GG了,页码半天显示不出来,于是使用RecyclerView作为容器,主要是看中RecyclerView的复用,不说了,看代

Android开发中RecyclerView组件使用的一些进阶技讲解_Android

RecyclerView的优势: 它自带ViewHolder来实现View的复用机制,再也不用ListView那样在getView()里自己写了 使用LayoutManager可以实现ListView,GridView以及流式布局的列表效果 通过setItemAnimator(ItemAnimator animator)可以实现增删动画(懒的话,可以使用默认的ItemAnimator对象,效果也不错) 控制item的间隔,可以使用addItemDecoration(ItemDecoration

Android开发中RecyclerView组件使用的一些进阶技讲解

RecyclerView的优势: 它自带ViewHolder来实现View的复用机制,再也不用ListView那样在getView()里自己写了 使用LayoutManager可以实现ListView,GridView以及流式布局的列表效果 通过setItemAnimator(ItemAnimator animator)可以实现增删动画(懒的话,可以使用默认的ItemAnimator对象,效果也不错) 控制item的间隔,可以使用addItemDecoration(ItemDecoration

android数据绑定框架介绍

github地址 地址 背景 数据绑定框架有很多,其实我就看过谷歌官方的数据绑定框架,官方的框架用起来的时候,觉得不是很顺手,侵入性还比较强.而且也一直纠结彷徨,从心底里质疑数据绑定框架的价值,到底给我们开发带来了什么,实用吗,可维护吗? 某一天的早晨突然灵光一现,决定自己去试试开发一个自己喜欢的数据绑定框架,经过没日没夜的艰苦风斗(当然是开玩笑的),中间反复修改设计,最后尘埃落定.虽然这个框架已经出世,也在生产环境试运行了一些页面,但是我对于数据绑定框架的价值,始终抱有质疑的态度,所以今天我将