您好,登錄后才能下訂單哦!
前言
首先不得不吐槽一下產品,尼瑪為啥要搞這樣的功能....搞個兩級的不就好了嘛...自帶控件,多好。三級,四級,聽說還有六級的....這樣喪心病狂的設計,后臺也不好給數據吧。
先看看效果:
兩級的效果:
三級的效果:
全部展開的效果(我只寫了五級)
說說為什么寫這貨吧:
公司產品提出三級這個需求后,我就在網上找啊找.
找的第一個,發現實現其實是ExpandListview嵌套.
找的第二個,ExpandRecyclview,然后就用唄,發現三級展開很卡,看源碼,
發現是RecyclerView套RecyclerView
就沒有不嵌套的么.....
然后找到hongyang的那個博客,寫個試試吧.
說說思路:
1.Treeadapter應該只需要關心List<TreeAdapterItem> datas 的內容
2.把每個item看成獨立的個體,布局樣式,每行所占比,bindViewHolder都由自己的來決定。
3.每一個item應該只關心自己的數據和自己的下一級的數據,不會去關心上上級,下下級
4.展開的實現,item把子數據集拿出來,然后添加到List<TreeAdapterItem> datas,變成與自己同級,因為每次展開只會展開一級數據。
5.折疊遞歸遍歷所有子數據,遞歸拿到自己所有的子數據集(可以理解因為一個文件夾下所有的文件,包括子文件夾下的所有),然后從List<TreeAdapterItem> datas刪除這些數據。
見代碼:
/** * Created by Jlanglang on 2016/12/7. * */ public abstract class TreeAdapterItem<D> { /** * 當前item的數據 */ protected D data; /** * 持有的子數據 */ protected List<TreeAdapterItem> childs; /** * 是否展開 */ protected boolean isExpand; /** * 布局資源id */ protected int layoutId; /** * 在每行中所占的比例 */ protected int spanSize; ···· get/set方法省略。。。。 ···· public TreeAdapterItem(D data) { this.data = data; childs = initChildsList(data); layoutId = initLayoutId(); spanSize = initSpansize(); } /** * 展開 */ public void onExpand() { isExpand = true; } /** * 折疊 */ public void onCollapse() { isExpand = false; } /** * 遞歸遍歷所有的子數據,包括子數據的子數據 * * @return List<TreeAdapterItem> */ public List<TreeAdapterItem> getAllChilds() { ArrayList<TreeAdapterItem> treeAdapterItems = new ArrayList<>(); for (int i = 0; i < childs.size(); i++) { TreeAdapterItem treeAdapterItem = childs.get(i); treeAdapterItems.add(treeAdapterItem); if (treeAdapterItem.isParent()) { List list = treeAdapterItem.getAllChilds(); if (list != null && list.size() > 0) { treeAdapterItems.addAll(list); } } } return treeAdapterItems; } /** * 是否持有子數據 * * @return */ public boolean isParent() { return childs != null && childs.size() > 0; } /** * item在每行中的spansize * 默認為0,如果為0則占滿一行 * 不建議連續的兩級,都設置該數值 * * @return 所占值 */ public int initSpansize() { return spanSize; } /** * 初始化子數據 * * @param data * @return */ protected abstract List<TreeAdapterItem> initChildsList(D data); /** * 該條目的布局id * * @return 布局id */ protected abstract int initLayoutId(); /** * 抽象holder的綁定 * * @param holder ViewHolder */ public abstract void onBindViewHolder(ViewHolder holder); }
再來看看Adapter
public class TreeRecyclerViewAdapter<T extends TreeAdapterItem> extends RecyclerView.Adapter<ViewHolder> { protected Context mContext; /** * 存儲所有可見的Node */ protected List<T> mDatas;//處理后的展示數據 /** * 點擊item的回調接口 */ private OnTreeItemClickListener onTreeItemClickListener; public void setOnTreeItemClickListener(OnTreeItemClickListener onTreeItemClickListener) { this.onTreeItemClickListener = onTreeItemClickListener; } /** * * @param context 上下文 * @param datas 條目數據 */ public TreeRecyclerViewAdapter(Context context, List<T> datas) { mContext = context; mDatas = datas; } /** * 相應RecyclerView的點擊事件 展開或關閉 * 重要 * @param position 觸發的條目 */ public void expandOrCollapse(int position) { //獲取當前點擊的條目 TreeAdapterItem treeAdapterItem = mDatas.get(position); //判斷點擊的條目有沒有下一級 if (!treeAdapterItem.isParent()) { return; } //判斷是否展開 boolean expand = treeAdapterItem.isExpand(); if (expand) { //獲取所有的子數據. List allChilds = treeAdapterItem.getAllChilds(); mDatas.removeAll(allChilds); //告訴item,折疊 treeAdapterItem.onCollapse(); } else { //獲取下一級的數據 mDatas.addAll(position + 1, treeAdapterItem.getChilds()); //告訴item,展開 treeAdapterItem.onExpand(); } notifyDataSetChanged(); } //adapter綁定Recycleview后. @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); //拿到布局管理器 RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); //判斷是否是GridLayoutManager,因為GridLayoutManager才能設置每個條目的行占比. if (layoutManager instanceof GridLayoutManager) { final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager; gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { TreeAdapterItem treeAdapterItem = mDatas.get(position); if (treeAdapterItem.getSpanSize() == 0) { //如果是默認的大小,則占一行 return gridLayoutManager.getSpanCount(); } //根據item的SpanSize來決定所占大小 return treeAdapterItem.getSpanSize(); } }); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //這里,直接通過item設置的id來創建Viewholder return ViewHolder.createViewHolder(mContext, parent, viewType); } @Override public void onBindViewHolder(ViewHolder holder, final int position) { final TreeAdapterItem treeAdapterItem = mDatas.get(position); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //折疊或展開 expandOrCollapse(position); if (onTreeItemClickListener != null) { //點擊監聽的回調.一般不是最后一級,不需要處理吧. onTreeItemClickListener.onClick(treeAdapterItem, position); } } }); treeAdapterItem.onBindViewHolder(holder); } @Override public int getItemViewType(int position) { //返回item的layoutId return mDatas.get(position).getLayoutId(); } @Override public int getItemCount() { return mDatas == null ? 0 : mDatas.size(); } public interface OnTreeItemClickListener { void onClick(TreeAdapterItem node, int position); } }
具體使用:
/** * Created by baozi on 2016/12/8. */ public class OneItem extends TreeAdapterItem<CityBean> { public OneItem(CityBean data) { super(data); } //這里數據用的是,一個三級城市列表數據。 @Override protected List<TreeAdapterItem> initChildsList(CityBean data) {//這個CityBean 是一級數據 ArrayList<TreeAdapterItem> oneChilds= new ArrayList<>(); List<CityBean.CitysBean> citys = data.getCitys(); if (citys == null) {//如果沒有二級數據就直接返回. return null; } for (int i = 0; i < citys.size(); i++) {//遍歷二級數據. TwoItem twoItem = new TwoItem(citys.get(i));//創建二級條目。 oneChilds.add(twoItem); } return oneChilds; } @Override protected int initLayoutId() {//當前級數的布局 return R.layout.itme_one; } @Override public void onExpand() { super.onExpand(); } @Override public void onBindViewHolder(ViewHolder holder) { //設置當前級數的viewhodler. //如果需要某個view展開關閉時的動畫,可以在這里保存view到成員變量。 //然后在onExpand()方法里面操作。 holder.setText(R.id.tv_content, data.getProvinceName()); } }
如果是同一級想要設置不同的布局,接著看
/** * Created by baozi on 2016/12/8. */ public class FourItem extends TreeAdapterItem<String> { .... @Override protected List<TreeAdapterItem> initChildsList(String data) { ArrayList<TreeAdapterItem> treeAdapterItems = new ArrayList<>(); for (int i = 0; i < 10; i++) { FiveItem threeItem = new FiveItem("我是五級"); //在遍歷的時候,通過條件,重設孩子的布局id.和所占比 if (i % 4 == 0) {//偷個懶,不多寫布局了. threeItem.setLayoutId(R.layout.itme_one); threeItem.setSpanSize(0); } else if (i % 3 == 0) { threeItem.setLayoutId(R.layout.item_two); threeItem.setSpanSize(2); } treeAdapterItems.add(threeItem); } return treeAdapterItems; } .... }
/** * Created by baozi on 2016/12/8. */ public class FiveItem extends TreeAdapterItem<String> { ....... //設置默認的布局 @Override protected int initLayoutId() { return R.layout.item_five; } //設置默認的占比 @Override public int initSpansize() { return 2; } //根據layoutId來判斷viewhodler并設置 @Override public void onBindViewHolder(ViewHolder holder) { if (layoutId == R.layout.itme_one) { holder.setText(R.id.tv_content, "我是第一種五級"); } else if (layoutId == R.layout.item_five) { holder.setText(R.id.tv_content, "我是第二種五級"); }else if (layoutId == R.layout.item_two) { holder.setText(R.id.tv_content, "我是第三種五級"); } } }
更新及詳解:
更深入的介紹可以查看這篇文章:https://www.jb51.net/article/113516.htm
下面附上Demo下載地址:
github傳送門:TreeRecyclerView
本地下載:http://xiazai.jb51.net/201705/yuanma/TreeRecyclerView(jb51.net).rar
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。