RecyclerView二级列表

就是常规意义上的那种二级列表,之所以写这篇文章,是因为就和我在专栏说明中说的一样,从0开始探索这个二级列表的功能,出现过各种意想不到的问题,也不难解决,但是浪费了大量的时间.

需求是这样的:开始没有数据,先添加一个一级列表的数据,然后开始一个一个往这个一级列表的子列表中添加数据,最新添加的数据放在最上面.子列表的Item中有一个Button,点击可以提交Item中EditText用户填写的信息,并在提交后隐藏Button,让EditText不可编辑.

严格来说我不是从0开始的哈,经过多番比较,我用了这位小哥的Demo,抽取基类的方式我非常欣赏,实际实现的时候写一个Adapter继承自基类,开关一级列表展示数据不要太方便,但是增加数据Demo中没有给出示例,我也是经过一番摸索,才实现.

稍微改动了一点的Adapter基类如下:


/**
 * @author XTER
 * @date 2019/8/9
 */
public abstract class FoldableRecyclerViewAdapter<K, V> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context mContext;

    /**
     * 上级布局
     */
    private int mGroupLayoutRes;
    /**
     * 下级布局
     */
    private int mChildLayoutRes;

    /**
     * 数据
     */
    private List<Unit<K, V>> mData;

    /**
     * 点击与长按监听接口
     */
    public interface OnItemClickLitener {
        void onItemClick(View view, int position);

        void onItemLongClick(View view, int position);
    }


    private OnItemClickLitener itemClickLitener;

    public void setOnItemClickLitener(OnItemClickLitener itemClickLitener) {
        this.itemClickLitener = itemClickLitener;
    }

    public FoldableRecyclerViewAdapter(Context mContext, int mGroupLayoutRes, int mChildLayoutRes, List<Unit<K, V>> mData) {
        this.mContext = mContext;
        this.mGroupLayoutRes = mGroupLayoutRes;
        this.mChildLayoutRes = mChildLayoutRes;
        if (mData == null) {
            this.mData = new ArrayList<>();
        } else {
            this.mData = mData;
        }
    }

    @Override
    public int getItemCount() {
        if (mSize == 0) {
            int totalSize = 0;
            for (Unit unit : mData) {
                totalSize += (unit.folded ? 1 : unit.children.size() + 1);
            }
            mSize = totalSize;
        }
        return mSize;
    }

    private int mSize = 0;

    @Override
    public int getItemViewType(int position) {
        //通过位置判断type,因为数据传入后顺序不变,可通过数据来判断当前位置是哪一类数据
        int currentPosition = -1;
        for (Unit unit : mData) {
            if (unit.folded) {
                currentPosition = currentPosition + 1;
                if (currentPosition == position) {
                    return FoldableViewHolder.GROUP;
                }
            } else {
                //算上group
                currentPosition = currentPosition + 1;
                if (currentPosition == position) {
                    return FoldableViewHolder.GROUP;
                }
                //算上children,通过比较大小确定是否是当前Unit中的child
                currentPosition = currentPosition + unit.children.size();
                if (position <= currentPosition) {
                    return FoldableViewHolder.CHILD;
                }
            }

        }
        return FoldableViewHolder.GROUP;
    }

    /**
     * 根据索引返回Unit中的K或V
     *
     * @param position 索引
     * @return K/V
     */
    public Object getItem(int position) {
        int currentPosition = -1;
        for (Unit unit : mData) {
            if (unit.folded) {
                currentPosition = currentPosition + 1;
                if (currentPosition == position) {
                    return unit.group;
                }
            } else {
                //算上group
                currentPosition = currentPosition + 1;
                if (currentPosition == position) {
                    return unit.group;
                }
                //算上children,通过计算确定是当前Unit的child的索引
                currentPosition = currentPosition + unit.children.size();
                if (position <= currentPosition) {
                    int unitChildIndex = unit.children.size() - 1 - (currentPosition - position);
                    return unit.children.get(unitChildIndex);
                }
            }
        }
        return null;
    }

    /**
     * 根据索引确定返回某个数据集
     *
     * @param position 索引
     * @return Unit
     */
    private Unit<K, V> getUnit(int position) {
        int currentPosition = -1;
        for (Unit<K, V> unit : mData) {
            //算上group
            currentPosition += unit.folded ? 1 : unit.children.size() + 1;
            if (position <= currentPosition)
                return unit;
        }
        return null;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
        if (viewType == FoldableViewHolder.CHILD) {
            return new ChildViewHolder(LayoutInflater.from(mContext).inflate(mChildLayoutRes, viewGroup, false));
        }
        return new GroupViewHolder(LayoutInflater.from(mContext).inflate(mGroupLayoutRes, viewGroup, false));
    }

    @Override
    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int position) {
        onBindView((FoldableViewHolder) viewHolder, position);
        viewHolder.itemView.setOnClickListener(v -> {
            if (viewHolder instanceof GroupViewHolder) {
                Unit<K, V> unit = getUnit(viewHolder.getAdapterPosition());
                unit.folded = !unit.folded;
                mSize = 0;
                LogUtils.d("onBindViewHolder: " + (viewHolder.getAdapterPosition() + 1) + "    " + unit.children.size());
//					notifyDataSetChanged();//最准确,但数据多时性能有影响
//					notifyItemRangeChanged(viewHolder.getAdapterPosition()+1,getItemCount());//需要考虑到holder的旧索引问题,暂无太好的办法去规避
                if (unit.folded) {
                    notifyItemRangeRemoved(viewHolder.getAdapterPosition() + 1, unit.children.size());
                } else {
                    notifyItemRangeInserted(viewHolder.getAdapterPosition() + 1, unit.children.size());
                }
            }
            if (itemClickLitener != null)
                itemClickLitener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition());
        });
        viewHolder.itemView.setOnLongClickListener(v -> {
            if (itemClickLitener != null)
                itemClickLitener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition());
            return true;
        });
    }

    public abstract void onBindView(FoldableViewHolder holder, int position);


    /*------------------ 关于数据的增删改  ------------------*/


    public void add(Unit<K, V> element) {
        mData.add(element);
        mSize = 0;
        notifyDataSetChanged();
    }

    public void add(List<Unit<K, V>> elemList) {
        mData.addAll(elemList);
        mSize = 0;
        notifyDataSetChanged();
    }

    public void remove(Unit<K, V> elem) {
        mData.remove(elem);
        mSize = 0;
        notifyDataSetChanged();
    }

    public void replace(List<Unit<K, V>> elemList) {
        mData = elemList;
        mSize = 0;
        notifyDataSetChanged();
    }


    /*------------------ 一些准备工作,定义数据或Holder之类  ------------------*/

    protected static abstract class FoldableViewHolder extends RecyclerView.ViewHolder {

        static final int GROUP = 0;
        static final int CHILD = 1;

        private SparseArray<View> views = new SparseArray<>();
        private View convertView;

        public FoldableViewHolder(@NonNull View itemView) {
            super(itemView);
            this.convertView = itemView;
        }

        @SuppressWarnings("unchecked")
        public <T extends View> T getView(int resId) {
            View v = views.get(resId);
            if (null == v) {
                v = convertView.findViewById(resId);
                views.put(resId, v);
            }
            return (T) v;
        }
    }

    protected static class GroupViewHolder extends FoldableViewHolder {

        public GroupViewHolder(@NonNull View itemView) {
            super(itemView);
        }

    }

    protected static class ChildViewHolder extends FoldableViewHolder {

        public ChildViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    /**
     * 数据实体,一对多
     *
     * @param <K>
     * @param <V>
     */
    public static class Unit<K, V> {
        public K group;
        public List<V> children;
        public boolean folded = false;

        public Unit(K group, List<V> children) {
            this.group = group;
            if (children == null) {
                this.children = new ArrayList<>();
            } else {
                this.children = children;
            }
        }

        public Unit(K group, List<V> children, boolean folded) {
            this(group, children);
            this.folded = folded;
        }
    }
}

泛型声明数据集,分开一级列表和二级列表,并且直接实现实现点击一级Item展开或收起对应二级列表的功能,你写一个继承它的子类,只用在onBinderView中分别给1级Item和2级Item绑定数据就可以了.

在Activity中声明声明数据集:

List<FoldableRecyclerViewAdapter.Unit<XXXBean, xxxBean>> mDataList = new ArrayList<>();

增加1个1级Item:

mDataList.add(new FoldableRecyclerViewAdapter.Unit(new XXXBean(), null));//创建不含2级Item的1级Item

增加1个2级Item:

mDataList.get(mDataList.size() - 1).children.add(0, new xxxBean());//在最后一个一级Item添加一个二级Item放在最前面

注意

为什么会浪费大量时间的原因所在.

原先在2级Item中有RadioGroup,点击不同的RadioButton,显示或隐藏EditText,我的实现方式是直接在OnBindView中给RadioGroup设置切换选项事件,在回调函数中让EditText显示或隐藏.这样导致的问题是界面状态混乱,因为回调函数被调用,也就是我们手动切换RadioGroup的时候,RecyclerView都已经刷新完整个界面了,这时候回调函数中的EditText对象还是那一个Item里面的对象吗?总结之,在onBindView中就是根据数据集展示Item,可以给子View设置各种事件,但是不能在事件中操作控件,只能自定义一个监听器,在回调函数中调用这个监听器,在Activity中更改数据集中某个状态值,然后调用notifyItemChanged方法,就可以立刻刷新这个Item.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值