当前位置:网站首页>使用RecyclerView实现三级折叠列表
使用RecyclerView实现三级折叠列表
2022-08-09 12:04:00 【谢栋_】
开题:RecyclerView可谓开发安卓比备的控件,用途跟强大之处自不用说,如果作为看官的你还不是太了解RecyclerView的具体使用,建议赶紧去补补知识。今天为大家分享的是在RecyclerView的基础之上扩展出三级折叠列表的具体实例,我还会跟以前的博文一样,结合我的实例代码跟大家边写边分析。
实现分析:
1.首先三级列表归根结底还是一个RecyclerView,只不过是在需要添加子布局的item下,插入一个Item,重新计算了,各个Item之间的位置关系,以及position的值。
2.三级列表需要根据三级之间的折叠关系,定义出不同的父、子ViewHolder,然后在Adapter根据type区分当前需要被插入的Item是属于第几级布局(三级父子孙布局关系)。
3.需要在父、子ViewHolder中的bindView中分别绑定不同的view视图,并做视图填充以及点击事件处理。
4.需要在Adapter中绑定Item点击回调接口,用于判断当前级(父子孙)视图下是需要add还是remove子View,并重新计算当前Item的currentPostion。
5.最后填充数据到Adapter,然后把Adapter绑定到RecyclerView上。
大体实现思路是这样,我跟大家一块屡了屡思路,好让各位看官心里有个路子,接下来,结合我的实例代码,边看边说哈。
首先是ExpandSectionAdapter.java:几乎核心主要是对该类的处理,里面实现了,对Item点击回调接口的监听,判断childView的显示隐藏,以及remove跟addview方法的实现,代码比较详细,我就不单独解说里面的方法了,代码贴上。
/**
* desc :可折叠扩展的Adapter
* author:xiedong
* data:2018/3/5
*/
public class ExpandSectionAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private Context context;
private List<SelectSectionParentEntity> dataBeanList;
private LayoutInflater mInflater;
private OnScrollListener mOnScrollListener;
public ExpandSectionAdapter(Context context, List<SelectSectionParentEntity> dataBeanList) {
this.context = context;
this.dataBeanList = dataBeanList;
this.mInflater = LayoutInflater.from(context);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType){
case SelectSectionParentEntity.PARENT_ITEM:
view = mInflater.inflate(R.layout.layout_section_list_parent, parent, false);
return new ParentViewHolder(context, view);
case SelectSectionParentEntity.CHILD_ITEM:
view = mInflater.inflate(R.layout.layout_section_list_child, parent, false);
return new ChildViewHolder(context, view);
default:
view = mInflater.inflate(R.layout.layout_section_list_parent, parent, false);
return new ParentViewHolder(context, view);
}
}
/**
* 根据不同的类型绑定View
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
switch (getItemViewType(position)){
case SelectSectionParentEntity.PARENT_ITEM:
ParentViewHolder parentViewHolder = (ParentViewHolder) holder;
parentViewHolder.bindView(dataBeanList.get(position), position, itemClickListener);
break;
case SelectSectionParentEntity.CHILD_ITEM:
ChildViewHolder childViewHolder = (ChildViewHolder) holder;
childViewHolder.bindView(dataBeanList.get(position), position);
break;
}
}
@Override
public int getItemCount() {
return dataBeanList.size();
}
@Override
public int getItemViewType(int position) {
return dataBeanList.get(position).getType();
}
private ExpandListClickListener itemClickListener = new ExpandListClickListener() {
@Override
public void onExpandChildren(SelectSectionParentEntity bean) {
int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
SelectSectionParentEntity children = getChildDataBean(bean);//获取要展示的子布局数据对象,注意区分onHideChildren方法中的getChildBean()。
if (children == null) {
return;
}
add(children, position + 1);//在当前的item下方插入
System.out.println(position+"------------");
if (position == dataBeanList.size() - 2 && mOnScrollListener != null) { //如果点击的item为最后一个
mOnScrollListener.scrollTo(position + 1);//向下滚动,使子布局能够完全展示
}
}
@Override
public void onHideChildren(SelectSectionParentEntity bean) {
int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
SelectSectionParentEntity children = bean.getChildBean();//获取子布局对象
if (children == null) {
return;
}
remove(position + 1);//删除
if (mOnScrollListener != null) {
mOnScrollListener.scrollTo(position);
}
}
};
/**
* 在父布局下方插入一条数据
* @param bean
* @param position
*/
public void add(SelectSectionParentEntity bean, int position) {
dataBeanList.add(position, bean);
notifyItemInserted(position);
}
/**
*移除子布局数据
* @param position
*/
protected void remove(int position) {
dataBeanList.remove(position);
notifyItemRemoved(position);
}
/**
* 确定当前点击的item位置并返回
* @param uuid
* @return
*/
protected int getCurrentPosition(String uuid) {
for (int i = 0; i < dataBeanList.size(); i++) {
if (uuid.equalsIgnoreCase(dataBeanList.get(i).getID())) {
return i;
}
}
return -1;
}
/**
* 封装子布局数据对象并返回
* 注意,此处只是重新封装一个DataBean对象,为了标注Type为子布局数据,进而展开,展示数据
* 要和onHideChildren方法里的getChildBean()区分开来
* @param bean
* @return
*/
private SelectSectionParentEntity getChildDataBean(SelectSectionParentEntity bean){
SelectSectionParentEntity child = new SelectSectionParentEntity();
child.setType(1);
child.setParentLeftTxt(bean.getParentLeftTxt());
child.setParentRightTxt(bean.getParentRightTxt());
child.setChildLeftTxt(bean.getChildLeftTxt());
child.setChildRightTxt(bean.getChildRightTxt());
return child;
}
/**
* 滚动监听接口
*/
public interface OnScrollListener{
void scrollTo(int pos);
}
public void setOnScrollListener(OnScrollListener onScrollListener){
this.mOnScrollListener = onScrollListener;
}
}
监听子View显示隐藏状态的点击回调接口:
/**
* desc :可折叠扩展的RecycleView回调接口
* author:xiedong
* data:2018/3/5
*/
public interface ExpandListClickListener {
/**
* 展开子Item
*
* @param entity
*/
void onExpandChildren(SelectSectionParentEntity entity);
/**
* 隐藏子Item
*
* @param entity
*/
void onHideChildren(SelectSectionParentEntity entity);
}
接下来是父、子ViewHodler,因为父子需要bind不同的视图 ,所有对父、子Viewholder进行分开处理,二者继承BaseViewHolder。
父ViewHodler.java:
/**
* desc :可折叠扩展的RecycleView Parent holder
* author:xiedong
* data:2018/3/5
*/
public class ParentViewHolder extends BaseViewHolder {
private Context mContext;
private View view;
private RelativeLayout containerLayout;
private TextView parentLeftView;
private TextView parentRightView;
private ImageView expand;
private View parentDashedView;
public ParentViewHolder(Context context, View itemView) {
super(itemView);
this.mContext = context;
this.view = itemView;
}
public void bindView(final SelectSectionParentEntity dataBean, final int pos, final ExpandListClickListener listener) {
containerLayout = (RelativeLayout) view.findViewById(R.id.container);
parentLeftView = (TextView) view.findViewById(R.id.tv_section_type_parent);
parentLeftView.setText(dataBean.getParentLeftTxt());
//父布局OnClick监听
containerLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
if (dataBean.isExpand()) {
listener.onHideChildren(dataBean);
// parentDashedView.setVisibility(View.VISIBLE);
dataBean.setExpand(false);
} else {
listener.onExpandChildren(dataBean);
// parentDashedView.setVisibility(View.INVISIBLE);
dataBean.setExpand(true);
}
}
}
});
}
}
子ViewHodler.java:
/**
* desc :可折叠扩展的RecycleView child holder
* author:xiedong
* data:2018/3/5
*/
public class ChildViewHolder extends BaseViewHolder {
private RelativeLayout containerLayout;
private Context mContext;
private View view;
private TextView childLeftText;
private TextView childRightText;
public ChildViewHolder(Context context, View itemView) {
super(itemView);
this.mContext = context;
this.view = itemView;
}
public void bindView(final SelectSectionParentEntity dataBean, final int pos) {
containerLayout = (RelativeLayout) view.findViewById(R.id.rl_child_container);
final LinearLayout grandChildContainer = (LinearLayout)view.findViewById(R.id.ll_gradnchild_container);
childLeftText = (TextView) view.findViewById(R.id.tv_section_type_child);
childLeftText.setText(dataBean.getChildLeftTxt());
containerLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
grandChildContainer.removeAllViews();
System.out.println("dfsa" + "---------------------");
GrandChildrenSectionView grandChildrenSectionView = new GrandChildrenSectionView(mContext);
grandChildContainer.addView(grandChildrenSectionView);
}
});
}
}
对viewHolder的处理比较简单,bingView之后,对view做填充以及点击事件处理即可,接下来二者继承的BaseViewHodler是我参考网上大神封装的BaseViewHodler,也贴上来分享给大家。
BaseViewHodler.java
public class BaseViewHolder extends ViewHolder {
private final SparseArray<View> views;
private final LinkedHashSet<Integer> childClickViewIds;
private final LinkedHashSet<Integer> itemChildLongClickViewIds;
public View convertView;
Object associatedObject;
public BaseViewHolder(View view) {
super(view);
AutoUtils.auto(view);
this.views = new SparseArray();
this.childClickViewIds = new LinkedHashSet();
this.itemChildLongClickViewIds = new LinkedHashSet();
this.convertView = view;
}
public HashSet<Integer> getItemChildLongClickViewIds() {
return this.itemChildLongClickViewIds;
}
public HashSet<Integer> getChildClickViewIds() {
return this.childClickViewIds;
}
public View getConvertView() {
return this.convertView;
}
public BaseViewHolder setText(int viewId, CharSequence value) {
TextView view = (TextView)this.getView(viewId);
view.setText(value);
return this;
}
public BaseViewHolder setText(int viewId, @StringRes int strId) {
TextView view = (TextView)this.getView(viewId);
view.setText(strId);
return this;
}
public BaseViewHolder setImageResource(int viewId, @DrawableRes int imageResId) {
ImageView view = (ImageView)this.getView(viewId);
view.setImageResource(imageResId);
return this;
}
public BaseViewHolder setBackgroundColor(int viewId, int color) {
View view = this.getView(viewId);
view.setBackgroundColor(color);
return this;
}
public BaseViewHolder setBackgroundRes(int viewId, @DrawableRes int backgroundRes) {
View view = this.getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public BaseViewHolder setTextColor(int viewId, int textColor) {
TextView view = (TextView)this.getView(viewId);
view.setTextColor(textColor);
return this;
}
public BaseViewHolder setImageDrawable(int viewId, Drawable drawable) {
ImageView view = (ImageView)this.getView(viewId);
view.setImageDrawable(drawable);
return this;
}
public BaseViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
ImageView view = (ImageView)this.getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
public BaseViewHolder setAlpha(int viewId, float value) {
if(VERSION.SDK_INT >= 11) {
this.getView(viewId).setAlpha(value);
} else {
AlphaAnimation alpha = new AlphaAnimation(value, value);
alpha.setDuration(0L);
alpha.setFillAfter(true);
this.getView(viewId).startAnimation(alpha);
}
return this;
}
public BaseViewHolder setVisible(int viewId, boolean visible) {
View view = this.getView(viewId);
view.setVisibility(visible?0:8);
return this;
}
public BaseViewHolder linkify(int viewId) {
TextView view = (TextView)this.getView(viewId);
Linkify.addLinks(view, 15);
return this;
}
public BaseViewHolder setTypeface(int viewId, Typeface typeface) {
TextView view = (TextView)this.getView(viewId);
view.setTypeface(typeface);
view.setPaintFlags(view.getPaintFlags() | 128);
return this;
}
public BaseViewHolder setTypeface(Typeface typeface, int... viewIds) {
int[] var3 = viewIds;
int var4 = viewIds.length;
for(int var5 = 0; var5 < var4; ++var5) {
int viewId = var3[var5];
TextView view = (TextView)this.getView(viewId);
view.setTypeface(typeface);
view.setPaintFlags(view.getPaintFlags() | 128);
}
return this;
}
public BaseViewHolder setProgress(int viewId, int progress) {
ProgressBar view = (ProgressBar)this.getView(viewId);
view.setProgress(progress);
return this;
}
public BaseViewHolder setProgress(int viewId, int progress, int max) {
ProgressBar view = (ProgressBar)this.getView(viewId);
view.setMax(max);
view.setProgress(progress);
return this;
}
public BaseViewHolder setMax(int viewId, int max) {
ProgressBar view = (ProgressBar)this.getView(viewId);
view.setMax(max);
return this;
}
public BaseViewHolder setRating(int viewId, float rating) {
RatingBar view = (RatingBar)this.getView(viewId);
view.setRating(rating);
return this;
}
public BaseViewHolder setRating(int viewId, float rating, int max) {
RatingBar view = (RatingBar)this.getView(viewId);
view.setMax(max);
view.setRating(rating);
return this;
}
/** @deprecated */
@Deprecated
public BaseViewHolder setOnClickListener(int viewId, OnClickListener listener) {
View view = this.getView(viewId);
view.setOnClickListener(listener);
return this;
}
public BaseViewHolder addOnClickListener(int viewId) {
this.childClickViewIds.add(Integer.valueOf(viewId));
return this;
}
public BaseViewHolder addOnLongClickListener(int viewId) {
this.itemChildLongClickViewIds.add(Integer.valueOf(viewId));
return this;
}
public BaseViewHolder setOnTouchListener(int viewId, OnTouchListener listener) {
View view = this.getView(viewId);
view.setOnTouchListener(listener);
return this;
}
public BaseViewHolder setOnLongClickListener(int viewId, OnLongClickListener listener) {
View view = this.getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
public BaseViewHolder setOnItemClickListener(int viewId, OnItemClickListener listener) {
AdapterView view = (AdapterView)this.getView(viewId);
view.setOnItemClickListener(listener);
return this;
}
public BaseViewHolder setOnItemLongClickListener(int viewId, OnItemLongClickListener listener) {
AdapterView view = (AdapterView)this.getView(viewId);
view.setOnItemLongClickListener(listener);
return this;
}
public BaseViewHolder setOnItemSelectedClickListener(int viewId, OnItemSelectedListener listener) {
AdapterView view = (AdapterView)this.getView(viewId);
view.setOnItemSelectedListener(listener);
return this;
}
public BaseViewHolder setOnCheckedChangeListener(int viewId, OnCheckedChangeListener listener) {
CompoundButton view = (CompoundButton)this.getView(viewId);
view.setOnCheckedChangeListener(listener);
return this;
}
public BaseViewHolder setTag(int viewId, Object tag) {
View view = this.getView(viewId);
view.setTag(tag);
return this;
}
public BaseViewHolder setTag(int viewId, int key, Object tag) {
View view = this.getView(viewId);
view.setTag(key, tag);
return this;
}
public BaseViewHolder setChecked(int viewId, boolean checked) {
View view = this.getView(viewId);
if(view instanceof CompoundButton) {
((CompoundButton)view).setChecked(checked);
} else if(view instanceof CheckedTextView) {
((CheckedTextView)view).setChecked(checked);
}
return this;
}
public BaseViewHolder setAdapter(int viewId, Adapter adapter) {
AdapterView view = (AdapterView)this.getView(viewId);
view.setAdapter(adapter);
return this;
}
public <T extends View> T getView(int viewId) {
View view = (View)this.views.get(viewId);
if(view == null) {
view = this.convertView.findViewById(viewId);
this.views.put(viewId, view);
}
return view;
}
public Object getAssociatedObject() {
return this.associatedObject;
}
public void setAssociatedObject(Object associatedObject) {
this.associatedObject = associatedObject;
}
}
到此基本上重要的工作已经做完了,剩下的就是给Adapter填充数据。我们模拟出两个实体类,分别用于给父、子view填充数据的实体,我省略set、get方法,只把属性贴上供大家参考。ParentEntity类:
public class SelectSectionParentEntity {
public static final int PARENT_ITEM = 0;//父布局
public static final int CHILD_ITEM = 1;//子布局
private int type;// 显示类型
private boolean isExpand;// 是否展开
private SelectSectionParentEntity childBean;
private String ID;
private String parentLeftTxt;
private String parentRightTxt;
private String childLeftTxt;
private String childRightTxt;
}
ChildEntity类:
public class SelectSectionChildEntity {
public static final int PARENT_ITEM = 0;//父布局
public static final int CHILD_ITEM = 1;//子布局
private int type;// 显示类型
private boolean isExpand;// 是否展开
private SelectSectionChildEntity childBean;
private String ID;
private String parentLeftTxt;
private String parentRightTxt;
private String childLeftTxt;
private String childRightTxt;
}
最后是模拟填充数据:
/**
* 模拟数据
*/
private void initData() {
dataBeanList = new ArrayList<>();
SelectSectionChildEntity childEntity = new SelectSectionChildEntity();
List<SelectSectionChildEntity> childEntityList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
sectionEntity = new SelectSectionParentEntity();
sectionEntity.setID(i + "");
sectionEntity.setType(0);
sectionEntity.setParentLeftTxt("父--" + i);
sectionEntity.setParentRightTxt("父内容--" + i);
sectionEntity.setChildLeftTxt("子--" + i);
sectionEntity.setChildRightTxt("子内容--" + i);
sectionEntity.setChildBean(sectionEntity);
//添加子布局
childEntity.setID(i + "");
childEntity.setType(0);
childEntity.setParentLeftTxt("父--" + i);
childEntity.setParentRightTxt("父内容--" + i);
childEntity.setChildLeftTxt("子--" + i);
childEntity.setChildRightTxt("子内容--" + i);
childEntity.setChildBean(childEntity);
childEntityList.add(childEntity);
dataBeanList.add(sectionEntity);
}
setData();
}
private void setData() {
trvSection.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new ExpandSectionAdapter(this, dataBeanList);
trvSection.setAdapter(mAdapter);
//滚动监听
mAdapter.setOnScrollListener(new ExpandSectionAdapter.OnScrollListener() {
@Override
public void scrollTo(int pos) {
trvSection.scrollToPosition(pos);
}
});
}
大体实现思路如上,我只是搭了一个简单的壳子,具体使用还需各位看官根据业务需求在此基础上做二次封装,如上分享,如有不妥之处,还请各位看官批评扶正。
边栏推荐
- [Microservice ~ Remote Call] Integrate RestTemplate, WebClient, Feign
- 2022 Niu Ke Duo School (6) M. Z-Game on grid
- 告别手摇织布机的AI时代
- Flutter入门进阶之旅(十)Dialog&Toast
- We really need DApp?Really can't meet our fantasy App?
- 在北极都可以穿短袖了,温度飙升至32.5℃
- 两分钟录音就可秒变语言通!火山语音音色复刻技术如何修炼而成?
- 标准C语言学习总结14
- 阿里高工带来的20022最新面试总结太香了
- LeetCode #101. Symmetric Binary Tree
猜你喜欢
专业人士使用的 11 种渗透测试工具
redis库没法引入
AQS Synchronization Component - FutureTask Analysis and Use Cases
合并两个有序列表
阻塞、非阻塞、多路复用、同步、异步、BIO、NIO、AIO 一锅端
ABAP interview questions: how to use the System CALL interface of the ABAP programming language, direct execution ABAP server operating System's shell command?
Two minutes recording can pass by second language!The volcano how to practice and become voice tone reproduction technology?
曲鸟全栈UI自动化教学(八):框架代码讲解和进一步优化
Flutter入门进阶之旅(八)Button Widget
900页数学论文证明旋转的黑洞不会爆炸,丘成桐:30多年来广义相对论首次重大突破...
随机推荐
超越CLIP的多模态模型,只需不到1%的训练数据!南加大最新研究来了
荣耀携手Blue Yonder,加快企业战略增长
WeChat payment development process
IDEA close/open reference prompt Usages
数据挖掘-06
ABP 6.0.0-rc.1的新特性
曲鸟全栈UI自动化教学(八):框架代码讲解和进一步优化
智驾科技完成C1轮融资,此前2轮已融4.5亿元
Apexsqlrecover cannot connect to database
李开复花上千万投的缝纫机器人,团队出自大疆
【小程序】低代码+小游戏=小游戏可视化开发
Manchester city launch emotional intelligence scarf can be detected, give the fans
C# Get system installed .NET version
Flutter入门进阶之旅(八)Button Widget
FFmpeg在win10上编译安装(配置libx264)
专业人士使用的 11 种渗透测试工具
微服务架构的核心关键点
[Interview high-frequency questions] Linked list high-frequency questions that can be gradually optimized
HAproxy:负载均衡
Blocking, non-blocking, multiplexing, synchronous, asynchronous, BIO, NIO, AIO all in one pot