欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-11-14 10:35:26
背景 随着公司业务越来越复杂,在同一个列表中需要展示各种类型的数据。 总体结构 itemviewadapter: 每种类型的卡片分别都是不同的it...

背景

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

总体结构

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

  • 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>

运行效果如下:

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

总的来说,实现一个多类型布局列表只需要写多个不同卡片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){

  }
}

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