当前位置:网站首页>使用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);
            }
        });
    }

大体实现思路如上,我只是搭了一个简单的壳子,具体使用还需各位看官根据业务需求在此基础上做二次封装,如上分享,如有不妥之处,还请各位看官批评扶正。

   

原网站

版权声明
本文为[谢栋_]所创,转载请带上原文链接,感谢
https://xiedong.blog.csdn.net/article/details/79621670