当前位置: 移动技术网 > IT编程>移动开发>Android > Android RecyclerView多类型布局卡片解决方案

Android RecyclerView多类型布局卡片解决方案

2019年07月23日  | 移动技术网IT编程  | 我要评论

最强大脑中国vs西班牙,李荣飚,简易车棚

背景

随着公司业务越来越复杂,在同一个列表中需要展示各种类型的数据。

总体结构

  • itemviewadapter: 每种类型的卡片分别都是不同的itemviewadapter
  • itemviewadapterfactory: 使用itemviewadapterfactory根据不同数据对应不同的itemviewadapter
  • multirecyclerviewadapter: multirecyclerviewadapter就是recylerview.adapter,并是个itemviewadapterfactory。
  • 具体只要继承multirecyclerviewadapter即可,实现itemviewadapterfactory中getviewtype、oncreateitemviewadapter两个方法
  • contextmap: 整个adapter共用一个contextmap数据上下文,用于外部(例fragment等)与itemadapter交互、itemadapter之间交互等一系列数据传递,可以解决参数层层传递的问题
  • recyclerviewholder: 通用recyclerview.viewholder,封装根据id获取view方法getview(viewid)、获取数据上下文方法getcontextmap()

使用方法

每种类型卡片item都实现itemviewadapter

package com.lkh.multiadapter;

import android.support.annotation.layoutres;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import java.util.list;

/**
 * 列表单项布局与数据绑定
 * created by luokanghui on 2017/5/24.
 */
public abstract class itemviewadapter<e>{

  /**
   * 返回列表单项view,如果view由资源layout加载而来,直接重写{@link #ongetlayoutid()}即可
   * @param parent 父view,一般为recyclerview
   * @return 列表单项view
   */
  public view oncreateview(viewgroup parent){
    return layoutinflater.from(parent.getcontext()).inflate(ongetlayoutid()
        , parent, false);
  }

  /**
   * 当recyclerviewholder创建成功后调用,只会调用一次
   * @param viewholder 单项view集合
   */
  public void oncreate(recyclerviewholder viewholder){

  }

  /**
   * 返回单项布局的资源id,如果重写了{@link #oncreateview(viewgroup)},则此方法可能失效
   * @return 单项布局layout id
   */
  @layoutres
  protected abstract int ongetlayoutid();

  /**
   * 把数据与view进行绑定,滑动时都会调用
   * @param viewholder 单项view集合
   * @param data 具体数据
   * @param position 在列表中的位置
   */
  public abstract void binddata(recyclerviewholder viewholder, e data, int position);

  /**
   * 局部更新时调用
   * @param viewholder 单项view集合
   * @param data 具体数据
   * @param position 在列表中的位置
   * @param payloads 局部更新标志,不会为空(isempty()==false)
   */
  public void binddata(recyclerviewholder viewholder, e data, int position, list<object> payloads){

  }
}

卡片1:

package com.lkh.multiadapter.sample;

import android.widget.textview;

import com.lkh.multiadapter.itemviewadapter;
import com.lkh.multiadapter.r;
import com.lkh.multiadapter.recyclerviewholder;

/**
 * 卡片1实现
 * created by luokanghui on 2019/3/18
 */
public class sampleoneitemviewadapter extends itemviewadapter<dataone> {
  @override
  protected int ongetlayoutid() {
    //布局layout资源id
    return r.layout.item_one;
  }

  @override
  public void binddata(recyclerviewholder viewholder, dataone data, int position) {
    //根据id获取view
    textview tvcontent = viewholder.getview(r.id.tv_content);
    //数据绑定
    tvcontent.settext(data.getcontent());
  }
}

package com.lkh.multiadapter.sample;

/**
 * 卡片1数据
 * created by luokanghui on 2019/3/18
 */
public class dataone {
  private string content;

  public string getcontent() {
    return content;
  }

  public void setcontent(string content) {
    this.content = content;
  }
}

item_one.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_height="wrap_content"
  android:background="#eeeeee"
  android:orientation="vertical">

  <textview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="卡片1"
    android:textcolor="#000000" />

  <textview
    android:id="@+id/tv_content"
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:gravity="center"
    android:textcolor="#000000" />

</linearlayout>

卡片2

package com.lkh.multiadapter.sample;

import android.widget.textview;

import com.lkh.multiadapter.itemviewadapter;
import com.lkh.multiadapter.r;
import com.lkh.multiadapter.recyclerviewholder;

/**
 * 卡片2实现
 * created by luokanghui on 2019/3/18
 */
public class sampletwoitemviewadapter extends itemviewadapter<datatwo> {
  @override
  protected int ongetlayoutid() {
    //布局layout资源id
    return r.layout.item_two;
  }

  @override
  public void binddata(recyclerviewholder viewholder, datatwo data, int position) {
    //根据id获取view
    textview tvnum = viewholder.getview(r.id.tv_num);
    //数据绑定
    tvnum.settext("num="+data.getnum());
  }
}
package com.lkh.multiadapter.sample;

/**
 * 卡片2数据
 * created by luokanghui on 2019/3/18
 */
public class datatwo {
  private int num;

  public int getnum() {
    return num;
  }

  public void setnum(int num) {
    this.num = num;
  }
}

item_two.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_height="wrap_content"
  android:background="#999999"
  android:orientation="vertical">

  <textview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="卡片2"
    android:textcolor="#0000ff" />

  <textview
    android:id="@+id/tv_num"
    android:layout_width="wrap_content"
    android:layout_height="100dp"
    android:gravity="center"
    android:textcolor="#0000ff" />

</linearlayout>

总adapter,继承multirecyclerviewadapter

package com.lkh.multiadapter.sample;

import com.lkh.multiadapter.itemviewadapter;
import com.lkh.multiadapter.multirecyclerviewadapter;

/**
 * 多布局adapter,根据不同data及position,使用不同itemviewadapter卡片
 * created by luokanghui on 2019/3/18
 */
public class samplemultiadapter extends multirecyclerviewadapter<object> {
  private static final int type_empty = 0;//空item
  private static final int type_one = 1;//卡片1
  private static final int type_two = 2;//卡片2

  @override
  public int getviewtype(object data, int position) {
    if (data instanceof dataone){//卡片1
      return type_one;
    }

    if (data instanceof datatwo){//卡片2
      return type_two;
    }

    return type_empty;//空item
  }

  @override
  public itemviewadapter oncreateitemviewadapter(int viewtype) {
    switch (viewtype){
      case type_one://卡片1
        return new sampleoneitemviewadapter();
      case type_two://卡片2
        return new sampletwoitemviewadapter();
      default://空item
        return new emptyitemviewadapter();
    }
  }
}

recyclerview中使用

package com.lkh.multiadapter;

import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.support.v7.widget.linearlayoutmanager;
import android.support.v7.widget.recyclerview;

import com.lkh.multiadapter.sample.dataone;
import com.lkh.multiadapter.sample.datatwo;
import com.lkh.multiadapter.sample.samplemultiadapter;

import java.util.arraylist;
import java.util.list;

public class mainactivity extends appcompatactivity {

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);

    recyclerview recyclerview = findviewbyid(r.id.recycler_view);
    recyclerview.setlayoutmanager(new linearlayoutmanager(this));

    samplemultiadapter adapter = new samplemultiadapter();

    //设置数据
    adapter.setdata(generatedata());

    //设置adapter
    recyclerview.setadapter(adapter);

  }

  //造测试数据
  private list<object> generatedata(){
    list<object> list = new arraylist<>();
    for (int i=0; i<20; i++){
      dataone dataone = new dataone();
      dataone.setcontent("这是卡片1数据:"+i);
      list.add(dataone);

      datatwo datatwo = new datatwo();
      datatwo.setnum(i);
      list.add(datatwo);
    }
    return list;
  }
}

activity_main.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_height="match_parent"
  android:orientation="vertical">

  <android.support.v7.widget.recyclerview
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

</linearlayout>

运行效果如下:

总的来说,实现一个多类型布局列表只需要写多个不同卡片itemviewadapter、继承multirecyclerviewadapter用来控制不同数据使用不同itemviewadapter,新增一个卡片只需要新增一个itemviewadapter,在multirecyclerviewadapter新加一项即可,不会影响其它卡片使用,而且itemviewadapter完全独立,可以很好的复用。

核心代码

package com.lkh.multiadapter;

import android.support.v7.widget.recyclerview;
import android.view.viewgroup;

import java.util.list;

/**
 * 多种布局adapter
 * created by luokanghui on 2017/5/24.
 */

public abstract class multirecyclerviewadapter<e> extends recyclerview.adapter<recyclerviewholder> implements itemviewadapterfactory<e> {
  public static final int no_type = -1;

  private list<e> datalist;

  protected final mapdata mmapdata = new mapdata();

  public multirecyclerviewadapter setdata(list<e> list) {
    this.datalist = list;
    return this;
  }

  @override
  public recyclerviewholder oncreateviewholder(viewgroup parent, int viewtype) {
    itemviewadapter itemviewmodule = oncreateitemviewadapter(viewtype);
    recyclerviewholder recyclerviewholder = new recyclerviewholder(itemviewmodule.oncreateview(parent), itemviewmodule, this, getcontextmap());
    itemviewmodule.oncreate(recyclerviewholder);
    return recyclerviewholder;
  }

  @override
  public void onbindviewholder(recyclerviewholder holder, int position) {
    if (checkitems(position)) {
      return;
    }
    holder.itemviewadapter.binddata(holder, datalist.get(position), position);
  }

  @override
  public void onbindviewholder(recyclerviewholder holder, int position, list<object> payloads) {
    if (checkitems(position)) {
      return;
    }
    if (payloads.isempty()) {
      super.onbindviewholder(holder, position, payloads);
    } else {
      holder.itemviewadapter.binddata(holder, datalist.get(position), position, payloads);
    }
  }

  @override
  public int getitemviewtype(int position) {
    if (checkitems(position)) {
      return no_type;
    }
    return getviewtype(datalist.get(position), position);
  }

  @override
  public int getitemcount() {
    return datalist == null ? 0 : datalist.size();
  }

  /**
   * true表示没通过
   */
  private boolean checkitems(int position) {
    return datalist == null || position < 0 || position >= datalist.size();
  }

  @override
  public mapdata getcontextmap() {
    return mmapdata;
  }
}
package com.lkh.multiadapter;
/**
 * 多布局itemviewadapter创建者
 * created by luokanghui on 2017/5/24.
 */
public interface itemviewadapterfactory<e> {

  /**
   * 返回itemviewadapter的类型
   * 建议根据data的数据类型判断不同的viewtype
   * @param data 具体数据
   * @param position 在列表中的位置
   * @return 类型
   */
  int getviewtype(e data, int position);

  /**
   * 根据不同的viewtype返回不同的itemviewadapter
   * @param viewtype 类型
   * @return itemviewadapter
   */
  itemviewadapter<? extends e> oncreateitemviewadapter(int viewtype);


  /**
   * 上下文数据
   * @return
   */
  mapdata getcontextmap();
}
package com.lkh.multiadapter;

import android.support.v7.widget.recyclerview;
import android.util.sparsearray;
import android.view.view;


/**
 * viewholder基类
 */
public final class recyclerviewholder extends recyclerview.viewholder {

  private final sparsearray<view> views;
  itemviewadapter itemviewadapter;
  private final recyclerview.adapter adapter;
  private final mapdata mmapdata ;


  public recyclerviewholder(view itemview, itemviewadapter itemviewadapter, recyclerview.adapter adapter, mapdata mapdata) {
    super(itemview);
    this.views = new sparsearray<>();
    this.itemviewadapter = itemviewadapter;
    this.adapter = adapter;
    this.mmapdata = mapdata;
  }

  /**
   * 根据id获取view,如果缓存中存在,直接使用缓存中的,避免重复执行findviewbyid
   */
  @suppresswarnings("unchecked")
  public <t extends view> t getview(int viewid) {
    view view = views.get(viewid);
    if (view == null) {
      view = itemview.findviewbyid(viewid);
      views.put(viewid, view);
    }
    return (t) view;
  }

  public recyclerview.adapter getadapter(){
    return adapter;
  }

  /**
   * 获取数据上下文
   */
  public mapdata getcontextmap(){
    return mmapdata;
  }
}
package com.lkh.multiadapter;

import android.support.annotation.layoutres;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;

import java.util.list;

/**
 * 列表单项布局与数据绑定
 * created by luokanghui on 2017/5/24.
 */

public abstract class itemviewadapter<e>{

  /**
   * 返回列表单项view,如果view由资源layout加载而来,直接重写{@link #ongetlayoutid()}即可
   * @param parent 父view,一般为recyclerview
   * @return 列表单项view
   */
  public view oncreateview(viewgroup parent){
    return layoutinflater.from(parent.getcontext()).inflate(ongetlayoutid()
        , parent, false);
  }

  /**
   * 当recyclerviewholder创建成功后调用,只会调用一次
   * @param viewholder 单项view集合
   */
  public void oncreate(recyclerviewholder viewholder){

  }

  /**
   * 返回单项布局的资源id,如果重写了{@link #oncreateview(viewgroup)},则此方法可能失效
   * @return 单项布局layout id
   */
  @layoutres
  protected abstract int ongetlayoutid();

  /**
   * 把数据与view进行绑定,滑动时都会调用
   * @param viewholder 单项view集合
   * @param data 具体数据
   * @param position 在列表中的位置
   */
  public abstract void binddata(recyclerviewholder viewholder, e data, int position);

  /**
   * 局部更新时调用
   * @param viewholder 单项view集合
   * @param data 具体数据
   * @param position 在列表中的位置
   * @param payloads 局部更新标志,不会为空(isempty()==false)
   */
  public void binddata(recyclerviewholder viewholder, e data, int position, list<object> payloads){

  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网