Android RecyclerView 数据绑定实例代码

前言

在上一个项目里有很多很多很多很多的RecyclerView,然后我需要写很多很多很多很多的Adapter和Viewholder——多倒没问题,但是里面有很多重复的代码这就不能忍了!每一个Adapter和ViewHolder其实做的事情非常的像:视图绑定,数据绑定,点击事件分发。还有啥?既然它们做的事情都一样,为啥我们还要傻傻的继续写着重复的代码?

正文

BaseAdapter

通常我们要创建一个RecyclerView.Adapter是怎么做的?

  1. 接收一个数据列表
  2. 重写getItemCount()方法,确定Item的个数
  3. 重写onCreateViewHolder()方法,绑定Layout,新建一个我们自己写的RecyclerView.ViewHolder
  4. 重写onBindViewHolder()方法,进行数据和视图绑定
  5. 由于RecyclerView没有写点击事件,把点击事件分发出去

基本上就是这个套路,或者再加一个refreshData()的方法——传新的数据进来然后notifyDataSetChanged()。基于这些点,我写了一个BaseAdapter基类:


/**
 * Adapter基类.
 * 适用于只有单个Item的RecyclerView.
 *
 * Created by lypeer on 16-5-24.
 */
public abstract class BaseAdapter<V> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

 /**
  * 装载了每个Item的Value的列表
  */
 private List<V> mValueList;

 /**
  * 我写的一个接口,通过回调分发点击事件
  */
 private OnItemClickListener<V> mOnItemClickListener;

 @Override
 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  return createViewHolder(parent.getContext(), parent);
 }

 @Override
 @SuppressWarnings("unchecked")//一定会是BaseViewHolder的子类,因为createViewHolder()的返回值
 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  //BaseViewHolder是我抽象出来的RecyclerView.ViewHolder的基类,下面会有详细讲解
  ((BaseViewHolder) holder).setData(mValueList.get(position), position, mOnItemClickListener);
 }

 /**
  * 设置每个Item的点击事件
  * @param listener
  */
 public void setOnClickListener(OnItemClickListener<V> listener) {
  this.mOnItemClickListener = listener;
 }

 /**
  * 刷新数据
  * @param valueList 新的数据列表
  */
 public void refreshData(List<V> valueList) {
  this.mValueList = valueList;
  notifyDataSetChanged();
 }

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

 /**
  * 生成ViewHolder
  * @param context
  * @param parent
  * @return
  */
 protected abstract BaseViewHolder createViewHolder(Context context, ViewGroup parent);
}

它的子类在继承它的时候需要指定泛型的具体类型,因为不同的Item也许其数据类型并不一样,这样就可以适应更多的Item。另外,其中提到了一个接口OnItemClickListener,这个接口很简单:

/**
 * 点击事件的接口
 * Created by lypeer on 16-5-24.
 */
public interface OnItemClickListener<V> {

 /**
  * 当item被点击的时候进行事件分发
  *
  * @param itemValue 点击的item传递的值
  * @param viewID 点击控件的id
  * @param position 被点击的item的位置
  */
 void onItemClick(V itemValue, int viewID, int position);
}

在使用它的时候同样需要使用泛型——原因和上面一样。

通过上面的BaseAdapter,我们把很多的共有操作都封装在了基类里面,而它的子类只需要根据需要新建不同的ViewHolder就行了——当然,这个viewHolder必须继承自BaseViewHolder,而BaseViewHolder是什么下面会有详细讲解。接下来是一个例子,假设我们现在在一个界面要有一个RecyclerView,它的每个Item的数据是一个String值,那么怎么使用我们的BaseAdapter简化开发过程呢?

public class SampleAdapter extends BaseAdapter<String> {
 @Override
 protected BaseViewHolder createViewHolder(Context context, ViewGroup parent) {
  //SampleViewHolder继承自BaseViewHolder
  return new SampleViewHolder(context, parent);
 }
}

是的,你没有看错!就只有这么几行代码!5秒完成!惊喜么?!

你只需要新建一个SampleAdapter继承自BaseAdapter,然后指定其泛型为String,再return new SampleViewHolder(context, parent) , 就完成了整个操作。

但是,有些读者也许会有疑惑:也许我们需要在SampleViewHolder里面做很多的操作呢?那岂不是只是把代码转换了一个地方而已,实质上并没有什么优化之处。

然而并不是。

BaseViewHolder

我一直认为,将ViewHolder写在Adapter里面是挺不明智的一个做法。这样的话Adapter这个类的职责太重了,它做得事情太多了,从数据接收到界面绑定,从控件初始化到点击事件分发,它简直什么都做完了。而这样是很不好的。很轻易的,一个比较复杂的RecyclerView的Adapter的代码就能达到成百上千行,这很可怕,这意味着这个类将变得冗杂且难以维护。那么怎么避免这种情况发生呢?我选择了将ViewHolder分离,同时加重ViewHolder的职责,使它们能比较均衡。

直接看BaseViewHolder的代码:

/**
 * ViewHolder基类
 *
 * Created by lypeer on 16-5-27.
 */
public abstract class BaseViewHolder<V> extends RecyclerView.ViewHolder {

 public BaseViewHolder(Context context, ViewGroup root, int layoutRes) {
  super(LayoutInflater.from(context).inflate(layoutRes, root, false));
  //这里使用了ButterKnife来进行控件的绑定
  ButterKnife.bind(this, itemView);
 }

 /**
  * 方便其子类进行一些需要Context的操作.
  *
  * @return 调用者的Context
  */
 public Context getContext() {
  return itemView.getContext();
 }

 /**
  * 抽象方法,绑定数据.
  * 让子类自行对数据和view进行绑定
  *
  * @param itemValue Item的数据
  * @param position 当前item的position
  * @param listener 点击事件监听者
  */
 protected abstract void bindData(V itemValue, int position, OnItemClickListener listener);

 /**
  * 用于传递数据和信息
  *
  * @param itemValue
  * @param position
  * @param listener
  */
 public void setData(V itemValue, int position, OnItemClickListener listener) {
  bindData(itemValue, position, listener);
 }
}

BaseViewHolder同样采用了泛型,以适应不同的数据类型。同时,我在BaseViewHolder里面使用了ButterKnife来简化代码,从此再也不用反反复复的findViewById了。

另外,大家可以看到BaseViewHolder里面的构造方法中传入了三个参数,但是在上面BaseAdapter的例子里面SampleViewHolder的构造方法我们却只传入了它的前两个构造参数,而第三个参数layoutRes并没有传进去,这是怎么回事呢?是上面写错了么?当然不是。BaseViewHolder的构造方法中有三个传参是因为它需要三个传参,而它的子类只有两个传参是因为它只能有两个传参。_BaseViewHolder必须要满足它的super构造,所以必须要有那三个参数,而它的子类如果那三个参数都是由外界传进来的,那么它怎么进行针对那个布局进行特异化的操作?它必须在类里面显式的指定Layout ID_——这其实是我很想优化的一个地方,因为这样的话子类继承BaseViewHolder之后还要修改它的构造方法,这是比较让人不省心的,但是目前还没有想到什么好点子来优雅地优化它。

BaseViewHolder里面有两个看起来很像的方法:setData()和bindData(),然而实际上除了传参相同,它们其他方面根本完全不一样。setData()方法是一个public的方法,可以由BaseViewHolder的子类的对象调用,其作用是从外部传入数据,以供ViewHolder所hold的那个view初始化,它可以说是一座传输信息的桥梁;而getData()是一个抽象的方法,它的具体实现在每一个BaseViewHolder的子类中,这些子类在这个方法里面进行控件的绑定和初始化,以及对控件的点击事件的处理等等。

说到点击事件的处理,它的子类应该怎么完成这件事呢?通过OnItemClickListener。我们可以借助于OnItemClickListener的接口回调来将需要处理的点击事件传递到外界,然后由外界进行处理。那么如果有多个控件的点击事件需要处理怎么办?不用担心,因为在OnItemClickListener的onClick方法中你需要传入点击的控件的id,这样一来就可以在外界进行对传入id的判断,从而针对不同的id执行不同的点击事件了。

接下来通过一个例子来看下具体怎么用,就是上面的那个SampleViewHolder吧:

public class SampleViewHolder extends BaseViewHolder<String> {

 //一个普通的可点击的TextView
 @Bind(R.id.is_tv_content)
 TextView mIsTvContent;

 public SampleViewHolder(Context context, ViewGroup root) {
  //修改了构造方法,在这里显式指定Layout ID
  super(context, root, R.layout.item_sample);
 }

 @Override
 protected void bindData(final String itemValue, final int position, final OnItemClickListener listener) {
  //在这里完成控件的初始化,将其与数据绑定
  if (itemValue != null) {
   mIsTvContent.setText(itemValue);
  }
  //如果需要有点击事件,就通过listener把它传递出去
  mIsTvContent.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    //如果外界没有调用BaseAdapter.setOnClickListener(),
    //listener就为null
    if(listener == null){
     return;
    }
    //listener不为null就将这个事件传递给外界处理
    listener.onItemClick(itemValue , v.getId() , position);
   }
  });
 }
}

同样很简单方便快捷清晰。但是还是有几点值得注意的地方。首先,不能忘了修改构造方法,显式的在super里面指定Layout ID,不然下一步都没法做,那就懵逼了。另外,在调用listener.onClick()方法的时候必须进行listener的验空——因为listener真的有可能为空!这样的话非常容易产生空异常导致程序崩溃。

宾果,就这样,SampleViewHolder就完成了,同样几乎不费吹灰之力。

结语

这篇博文的目的是分享一些我总结出来的东西,希望能让大家的开发加速那么一点点。当然,这里面也许还有我没发现的bug什么的,如果大家在使用的过程中发现了问题请不要客气,狠狠地砸给我吧!

最后还是再总结一下在有了BaseAdapter和BaseViewHolder的情况下怎么最快的搞定RecyclerView的那一套:

ViewHolder相关
新建 XXXViewHolder 继承自BaseViewHolder,指定泛型类型(也就是Item中数据的数据类型)。
删掉构造方法中的layoutRes参数,在super里面显式指定Layout ID。
用ButterKnife绑定控件。
在bindData()方法中完成控件的初始化以及点击事件的传递(别忘了listener的验空)

Adapter相关

新建 XXXAdapter 继承自BaseAdapter,指定泛型类型(也就是Item中数据的数据类型)。
return new XXXViewHolder(context, parent);

外界相关

绑定RecyclerView,新建XXXAdapter。
调用 BaseAdapter.refreshData()方法传入数据列表。
如果有对点击事件处理的需求,则调用BaseAdapter.setOnClickListener()方法。

目前我只做了针对RecyclerView中单个的Item的BaseAdapter,但是BaseViewHolder使可以通用的,并且其在多Item下也可以大大的简化Adapter的体积。

以上就是对Android RecyclerView 数据绑定的资料整理,后续继续补充相关资料谢谢大家对本站的支持!

(0)

相关推荐

  • Android ListView数据绑定显示的三种解决方法

    首先,创建一个用于显示一个item的layout,名为item.xml 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout

  • Android Data Binding数据绑定详解

    去年谷歌 I/O大会上介绍了一个非常厉害的新框架DataBinding, 数据绑定框架给我们带来了很大的方便,以前我们可能需要在每个Activity里写很多的findViewById,不仅麻烦,还增加了代码的耦合性,如果我们使用DataBinding,就可以抛弃那么多的findViewById,省时省力.说到这里,其实网上也有很多快速的注解框架,但是注解框架与DataBinding想比还是不好用,而且官网文档说DataBinding还能提高解析XML的速度,其实DataBinding的好用,不仅

  • 详解Android的MVVM框架 - 数据绑定

    本教程是跟着 Data Binding Guide学习过程中得出的一些实践经验,同时修改了官方教程的一些错误,每一个知识点都有对应的源码,争取做到实践与理论相结合. Data Binding 解决了 Android UI 编程中的一个痛点,官方原生支持 MVVM 模型可以让我们在不改变既有代码框架的前提下,非常容易地使用这些新特性.其实在此之前,已经有些第三方的框架可以支持 MVVM 模型,无耐由于框架的侵入性太强,导致一直没有流行起来. 准备 Android Studio 更新到 1.3 版

  • Android Studio绑定下拉框数据详解

    效果显示: 1.页面xml代码(项目的代码,直接复制会有错,自己修改一下就好) <TextView android:id="@+id/consultation_tv_section" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/consultation_et_doctor&qu

  • Android中 自定义数据绑定适配器BaseAdapter的方法

    复制代码 代码如下: public class PersonAdapter extends BaseAdapter { private List persons;// 要绑定的数据 private int resource;// 绑定的一个条目界面的id,此例中即为item.xml private LayoutInflater inflater;// 布局填充器,它可以使用一个xml文件生成一个View对象,可以通过Context获取实例对象 public PersonAdapter(Conte

  • Android中的SQL查询语句LIKE绑定参数问题解决办法(sqlite数据库)

    由于考虑到数据库的安全性,不被轻易SQL注入,执行查询语句时,一般不使用直接拼接的语句,而是使用参数传递的方法.然后在使用参数传递的方法中时,发现当使用like方式查询数据时,很容易出现一个问题. 错误案例: 复制代码 代码如下: String myname = "abc";String sql = "select * from mytable where name like '?%'";Cursor cursor = db.rawQuery(sql, new St

  • Android RecyclerView 数据绑定实例代码

    前言 在上一个项目里有很多很多很多很多的RecyclerView,然后我需要写很多很多很多很多的Adapter和Viewholder--多倒没问题,但是里面有很多重复的代码这就不能忍了!每一个Adapter和ViewHolder其实做的事情非常的像:视图绑定,数据绑定,点击事件分发.还有啥?既然它们做的事情都一样,为啥我们还要傻傻的继续写着重复的代码? 正文 BaseAdapter 通常我们要创建一个RecyclerView.Adapter是怎么做的? 接收一个数据列表 重写getItemCou

  • Android 自定义状态栏实例代码

    一.目标:Android5.0以上 二.步骤 1.在res-values-colors.xml下新建一个RGB颜色 <?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#3

  • Android 日期选择器实例代码

    废话不多说了,直接给大家贴代码了,具体代码如下所示: //出生年月设置 private void birthSetting() { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR, 1); new DatePickerDialog(mContext, new DatePickerDialog.OnDateSetListener() { @Override public void onDat

  • Android 滑动拦截实例代码解析

    废话不多说了,直接给大家贴代码了,具体代码如下所示: package demo.hq.com.fby; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by huqing on 2016/12/7.

  • Android 获取时间实例代码

     Android 获取时间实例代码 注意: h:12小时制小时数 H:24小时制小时数 实例代码: import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created by Administrator on 2017/5/8. */ public

  • Android 指纹功能实例代码

    最近在做项目的时候遇到了添加打开app图像解锁的功能,自己嘴欠说现在都用指纹功能,自己给自己挖了一个坑,真是没谁了 从网上看了一些资料,但是给我demo考虑的不是很多,设备支不支持都没考虑,如果支持的话是否添加过指纹也不知道,其实方法都很简单. 废话不多说,贴上工具类和使用方法 package com.tsm.test; import android.annotation.TargetApi; import android.app.Activity; import android.app.Key

  • Android Dialog对话框实例代码讲解

    Dialog的基本方法 //创建Dialog AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); //设置标题图标 builder.setIcon(R.drawable.ic_launcher); //设置标题 builder.setTitle("这是一个对话框"); //设置信息 builder.setMessage("是否要跳转?"); //确定按钮 setPosit

  • android自定义对话框实例代码

    1.实现效果    2.定义dialog.xml (res/layout/dialog.xml) <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android

  • android LinearLayout 布局实例代码

    复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?>  <!--      <LinearLayout>         线性版面配置,在这个标签中,所有元件都是按由上到下的排队排成的   --> <LinearLayout      xmlns:android="http://schemas.android.com/apk/res/android"    

  • Android实现webview实例代码

    webview是一个很简单的功能,代码没有什么逻辑上的难度,只是需要注意权限上的问题.其实在安卓编程的过程当中,权限问题可以算是出现的比较多的BUG. 1.MainAct package com.lxq.webview01; import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.view.View.O

随机推荐