溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

發(fā)布時間:2020-10-17 10:17:23 來源:腳本之家 閱讀:505 作者:Jlanglang 欄目:移動開發(fā)

前言

在本文開始之前請大家先看一下這篇文章:https://www.jb51.net/article/113510.htm

上面的這篇文章是之前寫的,里面發(fā)現(xiàn)有很多不好用地方,也學(xué)到些新姿勢,改動了許多地方。下面來看看詳細(xì)的介紹:

要點:

      1.可以通過后臺控制Item的展示.

      2.TreeRecyclerAdapter,可以展開,折疊.多級展示

      3.adapter可以使用裝飾者模式進(jìn)行擴(kuò)展.支持EmptyAdapter.可以添加headview和footview

      4.item的樣式可以編寫文檔,type與Class進(jìn)行對應(yīng),實現(xiàn)后臺控置,相同Item復(fù)用.

思路:(包含第一篇的思路)

      1.adapter應(yīng)該只需要關(guān)心List<baseItem> datas 的內(nèi)容

      2.把每個item看成獨立的個體. 布局樣式,每行所占比,onbindViewHolder由各個item實現(xiàn)。

      3.每一個item應(yīng)該只關(guān)心自己的數(shù)據(jù)和自己的下一級的數(shù)據(jù),不會去關(guān)心上上級,下下級

      4.展開的實現(xiàn),點擊時item把子數(shù)據(jù)拿出來,然后添加到adapter的datas中,變成同級,因為只會展開自己的下級數(shù)據(jù)。

      5.折疊的實現(xiàn),拿到下級數(shù)據(jù)(可以理解因為一個文件夾下文件),然后從adapter的datas中刪除這些數(shù)據(jù)。

      6.后臺控制可以通過初始化注冊的方法,將Item的Class注冊.保存到集合里

      7.后臺返回字段,獲取對應(yīng)class文件,通過Class.newInstance()方法構(gòu)建實例.

      8.將ViewHolder與Adapter寫成通用的,不需要再寫多個Adatper與ViewHolder,只需要寫多個Baseitem.與BaseItamData(JavaBean).

目錄介紹

+ 1.Adapter
 * Wapper------擴(kuò)展的wapper,
 * EmptyWapper --------當(dāng)無數(shù)據(jù)時顯示頁面.
 * HeaderAndFootWapper --------添加頭部view和尾部view

 - BaseRecyclerAdapter --------封裝的Adatper基類
 - ItemManager --------接口,管理Adatper刷新,增刪操作
 - TreeRecyclerAdapter ----多級列表,樹形結(jié)構(gòu)的adapter
 - TreeRecyclerViewType ----多級列表的顯示樣式,枚舉
 - ViewHolder----封裝的通用viewHodler

* 2.base
BaseItem<D extends BaseItemData> ------item的封裝
BaseItemData-----item的數(shù)據(jù)要求.javabean需要繼承該類.

* 3.factory
ItemConfig ----添加item的class,配置樣式
Itemfactory----通過class生成BaseItem的工廠類

* 4.view
TreeItem ----樹形列表的子item
TreeItemGroup ----樹形列表的父item
TreeParent---TreeItemGroup 實現(xiàn)該接口
TreeSelectItemGroup---可以選中子item的TreeItemGroup. demo:見購物頁面

來張丑丑的圖:

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

下面貼出部分代碼:

(一).BaseRecyclerAdapter :

/**
 * 普通BaseRecyclerAdapter,itme無父子關(guān)系.
 * 限定泛型為BaseItem的子類.
 * 通過BaseItem去處理ViewHolder
 */
public class BaseRecyclerAdapter<T extends BaseItem> extends
 RecyclerView.Adapter<ViewHolder> {

 private List<T> mDatas;//展示數(shù)據(jù)
 private ItemManager<T> mItemManager;
 private CheckItem mCheckItem;

 @Override
 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 //看源碼,這里的parent就是Recyclerview,所以不會為null.可以通過它拿到context
 return ViewHolder.createViewHolder(parent.getContext(), parent, viewType);
 }

 @Override
 public void onBindViewHolder(ViewHolder holder, int position) {
 T t = getDatas().get(position);
 //檢查是否綁定了ItemManage,因為item需要通過ItemManage告訴adapter刷新,增刪
 checkItemManage(t);
 //具體onBindViewHolder放到item里面去實現(xiàn)
 t.onBindViewHolder(holder);
 //實現(xiàn)點擊事件
 onBindViewHolderClick(holder);
 }
 /**
 * 實現(xiàn)item的點擊事件
 *
 * @param holder 綁定點擊事件的ViewHolder
 */
 public void onBindViewHolderClick(final ViewHolder holder) {
 //判斷當(dāng)前holder是否已經(jīng)設(shè)置了點擊事件
 if (!holder.itemView.hasOnClickListeners()) {
  holder.itemView.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   //獲得holder的position
   int layoutPosition = holder.getLayoutPosition();
   //檢查position是否可以點擊
   if (getCheckItem().checkPosition(layoutPosition)) {
   //檢查并得到真實的position
   int itemPosition = getCheckItem().getAfterCheckingPosition(layoutPosition);
   //拿到對應(yīng)item,回調(diào).
   getDatas().get(itemPosition).onClick();
   }
  }
  });
 }
 }
 /**
 * 這里將LayoutId作為type,因為LayoutId不可能相同,個人覺得可以作為item的標(biāo)志
 * @param position
 * @return
 */
 @Override
 public int getItemViewType(int position) {
 return mDatas.get(position).getLayoutId();
 }

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

 public List<T> getDatas() {
 if (mDatas == null) {
  mDatas = new ArrayList<>();
 }
 return mDatas;
 }

 /**
 * 需要手動setDatas(List<T> datas),否則數(shù)據(jù)為空
 * @param datas
 */
 public void setDatas(List<T> datas) {
 if (datas != null) {
  mDatas = datas;
  getItemManager().notifyDataSetChanged();
 }
 }
}

(二).TreeRecyclerAdapter

/**
 * Created by baozi on 2017/4/20.
 * 樹級結(jié)構(gòu)recycleradapter.
 * item之間有子父級關(guān)系,
 */

public class TreeRecyclerAdapter<T extends TreeItem> extends BaseRecyclerAdapter<T> {

 private TreeRecyclerViewType type;
 /**
 * 最初的數(shù)據(jù).沒有經(jīng)過增刪操作.
 */
 private List<T> initialDatas;


 @Override
 public void onBindViewHolderClick(final ViewHolder holder) {
 //判斷是否已有點擊監(jiān)聽
 if (!holder.itemView.hasOnClickListeners()) {
  holder.itemView.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   //獲得holder的LayoutPosition.
   int layoutPosition = holder.getLayoutPosition();
   //判斷是否需要點擊
   if (getCheckItem().checkPosition(layoutPosition)) {
   int itemPosition = getCheckItem().getAfterCheckingPosition(layoutPosition);
   //展開,折疊和item點擊不應(yīng)該同時響應(yīng)事件.
   if (type != TreeRecyclerViewType.SHOW_ALL) {
    //展開,折疊
    expandOrCollapse(itemPosition);
   } else {
    //點擊事件
    T item = getDatas().get(itemPosition);
    TreeItemGroup itemParentItem = item.getParentItem();
    //判斷上一級是否需要攔截這次事件,只處理當(dāng)前item的上級,不關(guān)心上上級如何處理.
    if (itemParentItem != null && itemParentItem.onInterceptClick(item)) {
    return;
    }
    item.onClick();
   }
   }
  }
  });
 }
 }

 @Override
 public void setDatas(List<T> datas) {
 //保存未修改過的List
 initialDatas = datas;
 //如果展開顯示全部,則需要遞歸處理添加所以的item
 if (type == TreeRecyclerViewType.SHOW_ALL) {
  for (int i = 0; i < datas.size(); i++) {
   T t = datas.get(i);
  getDatas().add(t);
  //判斷item是否是TreeItemGroup
  if (t instanceof TreeItemGroup) {
   List childs = ((TreeItemGroup) t).getAllChilds();
   if (childs != null) {
   //添加到item的后面.
   getDatas().addAll(childs);
   }
  }
  }
 } else {
  super.setDatas(datas);
 }
 }

 /**
 * 相應(yīng)RecyclerView的點擊事件 展開或關(guān)閉某節(jié)點
 *
 * @param position 觸發(fā)的條目
 */
 private void expandOrCollapse(int position) {
 T baseItem = getDatas().get(position);
 if (baseItem instanceof TreeItemGroup && ((TreeItemGroup) baseItem).isCanChangeExpand()) {
  TreeItemGroup treeParentItem = (TreeItemGroup) baseItem;
  boolean expand = treeParentItem.isExpand();
  List<T> allChilds = treeParentItem.getAllChilds();
  if (expand) {
  getDatas().removeAll(allChilds);
  treeParentItem.onCollapse();
  treeParentItem.setExpand(false);
  } else {
  getDatas().addAll(position + 1, allChilds);
  treeParentItem.onExpand();
  treeParentItem.setExpand(true);
  }
  getItemManager().notifyDataSetChanged();
 }
 }
}

(三).BaseItem(部分get.set代碼省略)

/**
 * Item的基類
 */
public abstract class BaseItem<D extends BaseItemData> {
 /**
 * 當(dāng)前item的數(shù)據(jù)
 */
 protected D data;
 /**
 * item在每行中的spansize
 * 默認(rèn)為0,如果為0則占滿一行
 *
 * @return 所占值, 比如recyclerview的列數(shù)為6, item需要占一半寬度, 就設(shè)置3
 */
 private int spanSize;
 /**
 * 可以通過ItemManager ,操作adatper
 * @return 
 */
 private ItemManager mItemManager;

 public int getLayoutId() {
 if (initLayoutId() <= 0) {
  throw new Resources.NotFoundException("請設(shè)置布局Id");
 }
 return initLayoutId();
 }

 /**
 * 子類需要實現(xiàn)該方法,并返回item的布局id
 *
 * @return 返回布局id.如果返回0,則會拋出異常
 */
 protected abstract int initLayoutId();

 /**
 * 抽象holder的綁定
 */
 public abstract void onBindViewHolder(ViewHolder viewHolder);

 /**
 * 當(dāng)前條目的點擊回調(diào)
 * 如果不需要點擊事件,則可以不復(fù)寫該方法.
 */
 public void onClick() {

 }

(四).TreeItem

/**
 * 組合模式
 * TreeRecyclerAdapter的item
 */
public abstract class TreeItem<D extends BaseItemData> extends BaseItem<D> {
 private TreeItemGroup parentItem;

 public void setParentItem(TreeItemGroup parentItem) {
 this.parentItem = parentItem;
 }

 /**
 * 獲取當(dāng)前item的父級
 *
 * @return
 */
 @Nullable
 public TreeItemGroup getParentItem() {
 return parentItem;
 }
}

(五).TreeItemGroup

/**
 * Created by baozi on 2016/12/22.
 * 擁有子集
 * 子集可以是parent,也可以是child
 */

public abstract class TreeItemGroup<D extends BaseItemData> extends TreeItem<D>
 implements TreeParent {
 /**
 * 持有的子item
 */
 private List<? extends BaseItem> childs;
 /**
 * 是否展開
 */
 private boolean isExpand;
 /**
 * 能否展開
 */
 protected boolean isCanChangeExpand = true;

 /**
 * 展開
 */
 @Override
 public void onExpand() {

 }

 /**
 * 折疊
 */
 @Override
 public void onCollapse() {

 }

 /**
 * 獲得自己的childs.
 * @return
 */
 @Nullable
 public List<? extends BaseItem> getChilds() {
 return childs;
 }
 /**
 * 獲得所有childs,包括子item的childs
 * @return
 */
 @Nullable
 public List<? extends BaseItem> getAllChilds() {
 if (getChilds() == null) {
  return null;
 }
 ArrayList<BaseItem> baseItems = new ArrayList<>();
 for (int i = 0; i < childs.size(); i++) {
  //下級
  BaseItem baseItem = childs.get(i);
  baseItems.add(baseItem);
  //判斷是否還有下下級,并且處于expand的狀態(tài)
  if (baseItem instanceof TreeItemGroup && ((TreeItemGroup) baseItem).isExpand()) {
  //調(diào)用下級的getAllChilds遍歷,相當(dāng)于遞歸遍歷
  List list = ((TreeItemGroup) baseItem).getAllChilds();
  if (list != null && list.size() > 0) {
   baseItems.addAll(list);
  }
  }
 }
 return baseItems;
 }

 public int getChildsCount() {
 return childs == null ? 0 : childs.size();
 }

 /**
 * 初始化子集
 *
 * @param data
 * @return
 */
 protected abstract List<? extends BaseItem> initChildsList(D data);

 /**
 * 是否消費child的click事件
 *
 * @param child 具體click的item
 * @return 返回true代表消費此次事件,child不會走onclick(),返回false說明不消費此次事件,child依然會走onclick()
 */
 public boolean onInterceptClick(TreeItem child) {
 return false;
 }

(六).TreeSelectItemGroup

/**
 * Created by baozi on 2016/12/22.
 * 可以選中子item的TreeItemGroup,點擊的item會保存起來.可以通過 getSelectItems()獲得選中item
 */
public abstract class TreeSelectItemGroup<D extends BaseItemData>
 extends TreeItemGroup<D> {
 /**
 * 選中的子item.只支持下一級,不支持下下級
 */
 private List<BaseItem> selectItems;

 public List<BaseItem> getSelectItems() {
 if (selectItems == null) {
  selectItems = new ArrayList<>();
 }
 return selectItems;
 }
 /**
 * 是否有選中item,
 * @return
 */
 public boolean isHaveCheck() {
 return !getSelectItems().isEmpty();
 }

 @Override
 public boolean onInterceptClick(TreeItem child) { 
 //單選
 if (selectFlag() == SelectFlag.SINGLE_CHOICE) {
  //如果已經(jīng)有選中的,則替換
  if (getSelectItems().size() != 0) {
  getSelectItems().set(0, child);
  } else {
  //沒有選中的則添加
  getSelectItems().add(child);
  }
 } else { 
  //判斷是否已添加.
  int index = getSelectItems().indexOf(child);
  if (index == -1) {//不存在則添加
  getSelectItems().add(child);
  } else {//存在則刪除
  getSelectItems().remove(index);
  }
 }
 return super.onInterceptClick(child);
 }

 /**
 * 必須指定選中樣式
 * @return
 */
 public abstract SelectFlag selectFlag();

 /**
 * 決定TreeSelectItemGroup的選中樣式
 */
 public enum SelectFlag {
 /**
  * 單選
  */
 SINGLE_CHOICE,
 /**
  * 多選
  */
 MULTIPLE_CHOICE
 }

具體的使用實例效果:

1.購物頁面:

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

Demo中的代碼:

//拿到數(shù)據(jù)
List<StoreBean> storeBean = initData();
//通過ItemFactory生成第一級Item,如果是通過后臺控制,則不需要傳Class
//List<ShopTitileItem> itemList = ItemFactory.createItemList(storeBean);
List<ShopTitileItem> itemList = ItemFactory.createItemList(storeBean, ShopTitileItem.class);
//創(chuàng)建TreeRecyclerAdapter
mAdapter = new TreeRecyclerAdapter<>();
//設(shè)置adapter的顯示樣式
mAdapter.setType(TreeRecyclerViewType.SHOW_ALL);
//將數(shù)據(jù)設(shè)置到Adapter中
mAdapter.setDatas(itemList);
//設(shè)置adapter
mRecyclerView.setAdapter(mAdapter);


/**
 * item的代碼
 * Created by baozi on 2016/12/22.
 */
public class ShopTitileItem extends TreeSelectItemGroup<StoreBean> {

 @Override
 protected List<? extends BaseItem> initChildsList(StoreBean data) {
 return ItemFactory.createTreeItemList(data.getShopListBeen(), this);
 }

 @Override
 protected int initLayoutId() {
 return R.layout.item_shopcart_title;
 }

 @Override
 public void onBindViewHolder(ViewHolder holder) {
 holder.setChecked(R.id.cb_ischeck, isHaveCheck());
 }

 @Override
 public void onClick() {
 if (!isHaveCheck()) {
  getSelectItems().clear();
  getSelectItems().addAll(getChilds());
 } else {
  getSelectItems().clear();
 }
 int size = getChilds().size();
 for (int i = 0; i < size; i++) {
  ShopListBean data = (ShopListBean) getChilds().get(i).getData();
  data.setCheck(isHaveCheck());
 }
 getItemManager().notifyDataSetChanged();
 }


 @Override
 public SelectFlag selectFlag() {
 return SelectFlag.MULTIPLE_CHOICE;
 }

 @Override
 public boolean canExpandOrCollapse() {
 return false;
 }

2.多級列表

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

Demo中的代碼:

 //拿到數(shù)據(jù)
 List<CityBean> cityBeen = initData();
//通過ItemFactory生成List<BaseItem>
List<OneTreeItemParent> itemList = ItemFactory.createItemList(cityBeen);
 TreeRecyclerAdapter treeRecyclerAdapter = new TreeRecyclerAdapter();
//設(shè)置數(shù)據(jù)
 treeRecyclerAdapter.setDatas(itemList);
 recyclerView.setAdapter(treeRecyclerAdapter);



/**
 *item的代碼
 * Created by baozi on 2016/12/8.
 */
public class OneTreeItemParent extends TreeItemGroup<CityBean> {
 @Override
 public List<? extends TreeItem> initChildsList(CityBean data) {
 return ItemFactory.createTreeItemList(data.getCitys(), TwoTreeItemParent.class, this);
 }

 @Override
 public int initLayoutId() {
 return R.layout.itme_one;
 }


 @Override
 public void onBindViewHolder(ViewHolder holder) {
 holder.setText(R.id.tv_content, data.getProvinceName());
 }

 @Override
 public boolean canExpandOrCollapse() {
 return false;
 }
}

3.多種type的列表

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

Android中RecyclerView實現(xiàn)多級折疊列表效果(二)

總結(jié):

      1.我覺得像購物車那種頁面挺復(fù)雜的,既然寫了多級列表,何不擴(kuò)展一個出來

      2.RecyclerView的點擊事件,看了很多封裝,發(fā)現(xiàn)很多都是每次onBindViewHolder去重新設(shè)置一遍,感覺挺不好的.

      3.我喜歡把數(shù)據(jù)集合讓Adatper去持有,然后通過Adapter進(jìn)行增刪改查操作.直接在Activity里持有數(shù)據(jù)集合進(jìn)行操作,我不是習(xí)慣(- -)

      4.用的習(xí)慣沒bug的才是好東西,如果你覺得實用或者能學(xué)到姿勢,就點個贊把,哈哈.

      5.大部分的邏輯都在Item中,由于拆分開了,會發(fā)現(xiàn)每個item的代碼也不會很多

      6.多級列表我已經(jīng)用在某個項目里了(- -),還沒發(fā)現(xiàn)什么問題(多級列表的簡單使用- -)

下面附上Demo.詳細(xì)代碼

github傳送門:TreeRecyclerView

本地下載:http://xiazai.jb51.net/201705/yuanma/TreeRecyclerView(jb51.net).rar

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI