难点

换种思路实现RecyclerView嵌套RecyclerView

2017-11-25  本文已影响0人  Horrarndoo

其实在实际开发中,难免会遇到一些类似于需要listview嵌套listview或者RecyclerView嵌套RecyclerView的界面需要实现。作为开发人员的我们,当然希望这种需求越少越好,但是如果偏偏就是有这种需求,用哪一种方式去实现比较好呢?

首先看一个很变态的界面效果图,估计很多人看到这个界面的第一眼就是懵逼的,这尼玛什么玩意?


动态效果

其实倒不是说实现图中的这种效果有多难,而是这种类似于嵌套的界面,实现的最终效果往往会达不到预期。
要实现这么个界面,根据网上大部分资料来说,无非就是下面这2种方法:

  1. 使用addView到布局的方式将item添加进布局容器中;
  2. RecyclerView嵌套RecyclerView(ListView嵌套ListView);

使用addView到布局的方式将item添加进布局容器中

其实这种方式是一种比较好的实现方式,使用这种方式实现的话,实际上是将item分为两类,一类是Normal item,一类是Group item。在Group item中添加一个空布局作为容器,然后根据child item的数量在adapter中使用addView的方式将child item添加到Group item中。但是这种方法也有一个弊端,就是child item数据刷新的时候,需要先将容器中的child全部清除,然后再重新添加新的child,这种就会导致数据刷新的时候界面明显有一个先清空再添加的界面变化,对用户体验上来讲是很不友好的。

RecyclerView嵌套RecyclerView(ListView嵌套ListView)

ListView嵌套ListView网上有很多相关的资料,这种方式首先存在的问题就是item只显示一条的问题。然后随便一查,就能查到这么一条解决方法,就是重写ListView的onMeasure方法:

@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);  
    super.onMeasure(widthMeasureSpec, expandSpec);  
}  

RecyclerView嵌套RecyclerView的话就直接在Group item布局中添加一条child的RecyclerView然后绑定child adapter就可以了。虽然这样实现看起来很简单,但是不论是ListView嵌套ListView也好,RecyclerView嵌套RecyclerView也好,滑动的时候,由于要计算child的高度,最直观的体现就是滑动卡顿。然后还有一个问题就是这种嵌套的方式item的viewHolder无法复用,数据量大到一定程度就OOM了。

单RecyclerView实现

既然上面两种方式都有弊端,而且相对比较影响体验。我们不妨换一种思路来实现这种布局:使用单RecyclerView实现。
前面两种方式针对的是控件,现在既然要使用单RecyclerView实现,那么我们的做法就是从数据层面做修改,也就是不论Normal item、Group item、Child item,都只是作为一个RecyclerView的一种布局来实现,而不是将child作为Group item的子布局来实现。

首先看看模拟的实体类:

NormalItemBean

public class NormalItemBean {
    private String title;
    private int itemId;
    private boolean isChecked;

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}

GroupItemBean

public class GroupItemBean {
    private String title;
    private boolean isChecked;
    private int itemId;
    private List<ChildItemBean> childs;

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public List<ChildItemBean> getChilds() {
        return childs;
    }

    public void setChilds(List<ChildItemBean> childs) {
        this.childs = childs;
    }

    public String getTitle() {

        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}

ChildItemBean

public class ChildItemBean {
    private String title;
    private boolean isChecked;
    private int groupId;
    private int itemId;

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public int getGroupId() {
        return groupId;
    }

    public void setGroupId(int groupId) {
        this.groupId = groupId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}

假设这3个实体类都是服务器返回的数据,根据我们的思路,Group、Child、Normal都是只是RecyclerView的不同类型的item而已,所以这里对实体类做一下调整,调整后的实体类如下:

public class DemoItemBean {
    public static final int TYPE_NORMAL = 0;
    public static final int TYPE_GROUP = 1;
    public static final int TYPE_CHILD = 2;

    private String title;
    private boolean isChecked;
    private int itemType;
    private int itemId;

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getItemType() {
        return itemType;
    }

    public void setItemType(int itemType) {
        this.itemType = itemType;
    }
}

public class NormalItemBean extends DemoItemBean {
}

public class GroupItemBean extends DemoItemBean{
    private List<ChildItemBean> childs;

    public List<ChildItemBean> getChilds() {
        return childs;
    }

    public void setChilds(List<ChildItemBean> childs) {
        this.childs = childs;
    }
}


public class ChildItemBean extends DemoItemBean{
    private int groupId;

    public int getGroupId() {
        return groupId;
    }

    public void setGroupId(int groupId) {
        this.groupId = groupId;
    }
}

接下来写模拟数据的处理,将Normal、Group、Child整合成一个list。

public class ParseHelper {
    //=========== 这里模拟服务器返回的数据 ==========
    private static List<NormalItemBean> getNormalDatas() {
        List<NormalItemBean> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            NormalItemBean bean = new NormalItemBean();
            bean.setItemId(i);
            bean.setChecked(false);
            bean.setItemType(DemoItemBean.TYPE_NORMAL);
            bean.setTitle("Normal: " + i);
            list.add(bean);
        }
        return list;
    }

    private static List<GroupItemBean> getGroupDatas() {
        List<GroupItemBean> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            List<ChildItemBean> childList = new ArrayList<>();
            GroupItemBean bean = new GroupItemBean();
            bean.setItemId(i);
            bean.setItemType(DemoItemBean.TYPE_GROUP);
            bean.setTitle("Group: " + i);
            bean.setChecked(false);

            for (int j = 0; j < 3; j++) {
                ChildItemBean bean1 = new ChildItemBean();
                bean1.setTitle("group: " + i + " child: " + j);
                bean1.setChecked(false);
                bean1.setItemType(DemoItemBean.TYPE_CHILD);
                bean1.setGroupId(i);//child的groupId对应Group的itemId
                bean1.setItemId(bean.getItemId());//child的itemId和其父group的itemId一致
                childList.add(bean1);
            }
            bean.setChilds(childList);
            list.add(bean);
        }
        return list;
    }
    //===============================================

    public static List<DemoItemBean> getParseDatas() {
        List<DemoItemBean> list = new ArrayList<>();

        for (NormalItemBean bean : getNormalDatas()) {
            list.add(bean);//normal
        }

        for (GroupItemBean bean : getGroupDatas()) {
            list.add(bean);//group

            for (ChildItemBean bean1 : bean.getChilds()) {
                list.add(bean1);//child
            }
        }
        return list;
    }
}

接下来完成RecyclerView的Adapter。

public class DemoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<DemoItemBean> mDatas;
    private Context mContext;
    private OnCheckChangeListener onCheckChangeListener;

    public void setOnCheckChangeListener(OnCheckChangeListener l) {
        onCheckChangeListener = l;
    }

    public DemoAdapter(Context context, List<DemoItemBean> datas) {
        mContext = context;
        mDatas = datas;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.w("tag", "onCreateViewHolder");
        LayoutInflater mInflater = LayoutInflater.from(mContext);
        RecyclerView.ViewHolder holder = null;
        switch (viewType) {
            case DemoItemBean.TYPE_NORMAL:
                View v = mInflater.inflate(R.layout.item_normal, parent, false);
                holder = new NormalViewHolder(v);
                break;
            case DemoItemBean.TYPE_GROUP:
                View v1 = mInflater.inflate(R.layout.item_group, parent, false);
                holder = new GroupViewHolder(v1);
                break;
            case DemoItemBean.TYPE_CHILD:
                View v2 = mInflater.inflate(R.layout.item_child, parent, false);
                holder = new ChildViewHolder(v2);
                break;
        }
        return holder;
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        Log.w("tag", "onBindViewHolder");
        if (holder instanceof NormalViewHolder) {
            NormalViewHolder nHolder = (NormalViewHolder) holder;
            nHolder.bindData((NormalItemBean) mDatas.get(position));
            nHolder.tvNormal.setText(mDatas.get(position).getTitle());
            nHolder.cbNormal.setOnCheckedChangeListener(new OnCheckedChangeListener(position,
                    DemoItemBean.TYPE_NORMAL));
            nHolder.cbNormal.setChecked(mDatas.get(position).isChecked());
        } else if (holder instanceof GroupViewHolder) {
            GroupViewHolder gHolder = (GroupViewHolder) holder;
            gHolder.bindData((GroupItemBean) mDatas.get(position));
            gHolder.tvGroup.setText(mDatas.get(position).getTitle());
            gHolder.cbGroup.setOnCheckedChangeListener(new OnCheckedChangeListener(position,
                    DemoItemBean.TYPE_GROUP));
            gHolder.cbGroup.setChecked(mDatas.get(position).isChecked());
        } else if (holder instanceof ChildViewHolder) {
            ChildViewHolder cHolder = (ChildViewHolder) holder;
            cHolder.bindData((ChildItemBean) mDatas.get(position));
            cHolder.tvChild.setText(mDatas.get(position).getTitle());
            cHolder.cbChild.setOnCheckedChangeListener(new OnCheckedChangeListener(position,
                    DemoItemBean.TYPE_CHILD));
            cHolder.cbChild.setChecked(mDatas.get(position).isChecked());
        }
    }

    @Override
    public int getItemViewType(int position) {
        return mDatas.get(position).getItemType();
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    /**
     * CheckBox CheckedChangeListener
     */
    private class OnCheckedChangeListener implements CompoundButton.OnCheckedChangeListener {
        int mPosition, mItemType;

        public OnCheckedChangeListener(int position, int itemType) {
            mPosition = position;
            mItemType = itemType;
        }

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (onCheckChangeListener != null)
                onCheckChangeListener.onCheckedChanged(mDatas, mPosition, isChecked, mItemType);
        }
    }
}

三个item的ViewHolder:

public class NormalViewHolder extends RecyclerView.ViewHolder {
    private NormalItemBean bean;
    public TextView tvNormal;
    public LinearLayout llNormal;
    public CheckBox cbNormal;

    public NormalViewHolder(View itemView) {
        super(itemView);
        tvNormal = (TextView) itemView.findViewById(R.id.tv_normal);
        llNormal = (LinearLayout) itemView.findViewById(R.id.ll_normal);
        cbNormal = (CheckBox) itemView.findViewById(R.id.cb_normal);
        llNormal.setOnClickListener(new OnClickListener());
    }

    /**
     * 绑定item数据
     * @param bean item数据
     */
    public void bindData(NormalItemBean bean){
        this.bean = bean;
    }

    private class OnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.ll_normal:
                    ToastUtils.showToast(bean.getTitle() + " is clicked.");
                    break;
            }
        }
    }
}

public class GroupViewHolder extends RecyclerView.ViewHolder {
    private GroupItemBean bean;
    public TextView tvGroup, tvSub1, tvSub2, tvSub3;
    public CheckBox cbGroup;
    public LinearLayout llGroup, subEdit;

    public GroupViewHolder(View itemView) {
        super(itemView);
        tvGroup = (TextView) itemView.findViewById(R.id.tv_group);
        cbGroup = (CheckBox) itemView.findViewById(R.id.cb_group);
        llGroup = (LinearLayout) itemView.findViewById(R.id.ll_group);
        subEdit = (LinearLayout) itemView.findViewById(R.id.sub_edit);
        tvSub1 = (TextView) itemView.findViewById(R.id.tv_sub_1);
        tvSub2 = (TextView) itemView.findViewById(R.id.tv_sub_2);
        tvSub3 = (TextView) itemView.findViewById(R.id.tv_sub_3);

        llGroup.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                subEdit.setVisibility(View.VISIBLE);
                return true;
            }
        });

        llGroup.setOnClickListener(new OnClickListener());
        tvSub1.setOnClickListener(new OnClickListener());
        tvSub2.setOnClickListener(new OnClickListener());
        tvSub3.setOnClickListener(new OnClickListener());
    }

    /**
     * 绑定item数据
     * @param bean item数据
     */
    public void bindData(GroupItemBean bean){
        this.bean = bean;
    }

    private class OnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            subEdit.setVisibility(View.GONE);
            switch (v.getId()) {
                case R.id.ll_group:
                    ToastUtils.showToast(bean.getTitle() + " is clicked.");
                    break;
                case R.id.tv_sub_1:
                    ToastUtils.showToast(bean.getTitle() + " subItem 1 is clicked.");
                    break;
                case R.id.tv_sub_2:
                    ToastUtils.showToast(bean.getTitle() + " subItem 2 is clicked.");
                    break;
                case R.id.tv_sub_3:
                    ToastUtils.showToast(bean.getTitle() + " subItem 3 is clicked.");
                    break;
            }
        }
    }
}

public class ChildViewHolder extends RecyclerView.ViewHolder {
    private ChildItemBean bean;
    public TextView tvChild;
    public CheckBox cbChild;
    public LinearLayout llChild;

    public ChildViewHolder(View itemView) {
        super(itemView);

        tvChild = (TextView) itemView.findViewById(R.id.tv_child);
        cbChild = (CheckBox) itemView.findViewById(R.id.cb_child);
        llChild = (LinearLayout) itemView.findViewById(R.id.ll_child);

        llChild.setOnClickListener(new OnClickListener());
    }

    /**
     * 绑定item数据
     *
     * @param bean item数据
     */
    public void bindData(ChildItemBean bean) {
        this.bean = bean;
    }

    private class OnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.ll_child:
                    ToastUtils.showToast(bean.getTitle() + " is clicked.");
                    break;
            }
        }
    }
}

这样基本的单RecyclerView实现嵌套布局就实现了,其实整体实现起来还是挺简单的,child通过itemId和对应的Group绑定,具体的item类型根据itemType来区分。
然后增加一些好玩的功能,包括选中item、添加item以及删除选中item等,具体效果如下图。


item点击 item选中
添加item 删除item

其实这些功能都是一个购物车界面比较常见的功能,只是刚好在这里顺带实现了。实现起来还是没什么特别好说的,直接贴上一些关键代码吧。

public class DemoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

     ……
    
    /**
     * 删除选中item
     */
    public void removeChecked() {
        int iMax = mDatas.size() - 1;
        //这里要倒序,因为要删除mDatas中的数据,mDatas的长度会变化
        for (int i = iMax; i >= 0; i--) {
            if (mDatas.get(i).isChecked()) {
                mDatas.remove(i);
                notifyItemRemoved(i);
                notifyItemRangeChanged(0, mDatas.size());
            }
        }
    }

    /**
     * 添加 Normal item
     */
    public void addNormal() {
        int addPosition = 0;
        int itemId = 0;
        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
                addPosition = i;//得到要插入的position
                break;
            }
        }

        if (!isHaveGroup()) {//如果列表中没有group,直接在list末尾添加item
            if (addPosition == 0) {
                addPosition = mDatas.size();
            }
        }

        if (addPosition > 0) {
            itemId = mDatas.get(addPosition - 1).getItemId() + 1;
        }

        mDatas.add(addPosition, ParseHelper.newNormalItem(itemId));
        notifyItemInserted(addPosition);//通知演示插入动画
        notifyItemRangeChanged(addPosition, mDatas.size() - addPosition);//通知数据与界面重新绑定
    }

    /**
     * 添加 Group item
     */
    public void addGroup() {
        int addPosition = mDatas.size();
        int itemId = 0;

        if (isHaveGroup()) {
            for (int i = 0; i < mDatas.size(); i++) {
                if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
                    itemId = mDatas.get(i).getItemId() + 1;
                }
            }
        }

        mDatas.add(addPosition, ParseHelper.newGroupItem(itemId));
        notifyItemInserted(addPosition);//通知演示插入动画
        notifyItemRangeChanged(addPosition, mDatas.size() - addPosition);//通知数据与界面重新绑定
    }

    /**
     * 添加 Child item
     * <p>
     * child item添加位置永远归属于最后一个Group item
     */
    public void addChild() {
        int addPosition = 0;
        int itemId = 0;
        int childId = 0;

        if (!isHaveGroup() || mDatas.get(mDatas.size() - 1).getItemType() == DemoItemBean
                .TYPE_NORMAL) {
            addGroup();
        }

        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
                itemId = mDatas.get(i).getItemId();
            }
        }

        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).getItemId() == itemId && mDatas.get(i).getItemType() ==
                    DemoItemBean.TYPE_CHILD) {
                childId++;
            }
        }

        addPosition = mDatas.size();
        mDatas.add(addPosition, ParseHelper.newChildItem(mDatas, itemId, childId));
        notifyItemInserted(addPosition);//通知演示插入动画
        notifyItemRangeChanged(addPosition, mDatas.size() - addPosition);//通知数据与界面重新绑定

        if (onCheckChangeListener != null)
            onCheckChangeListener.onCheckedChanged(mDatas, addPosition, mDatas.get(addPosition)
                    .isChecked(), DemoItemBean.TYPE_CHILD);
    }

    /**
     * 当前list是否含有group
     *
     * @return 当前list是否含有group
     */
    private boolean isHaveGroup() {
        boolean isHaveGroup = false;//当前列表是否包含group

        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
                isHaveGroup = true;
                break;
            }
        }
        return isHaveGroup;
    }

    /**
     * 获取最后一个Normal item的position
     *
     * @return 最后一个Normal item的position
     */
    public int getLastNormalItemPosition() {
        int addPosition = 0;
        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
                addPosition = i;
                break;
            }
        }

        if (addPosition == 0) {
            addPosition = mDatas.size();
        }

        return addPosition - 1;
    }

    /**
     * 获取最后一个item的position
     *
     * @return 最后一个item的position
     */
    public int getLastItemPosition() {
        return mDatas.size();
    }
}

public class ParseHelper {

    ……

    /**
     * 获取group下的child list
     *
     * @param beans    整个数据list
     * @param position 当前group的position
     */
    public static List<ChildItemBean> getChildList(List<DemoItemBean> beans, int position) {
        List<ChildItemBean> childList = new ArrayList<>();
        for (DemoItemBean bean : beans) {
            //item id不相同直接跳过
            if (bean.getItemId() != beans.get(position).getItemId())
                continue;

            if (bean.getItemType() == DemoItemBean.TYPE_CHILD) {
                childList.add((ChildItemBean) bean);
            }
        }
        return childList;
    }

    /**
     * 取出list中的groupBean
     *
     * @param beans
     * @param itemId
     * @return
     */
    public static GroupItemBean getGroupBean(List<DemoItemBean> beans, int itemId) {
        for (DemoItemBean bean : beans) {
            if (bean.getItemType() == DemoItemBean.TYPE_GROUP && bean.getItemId() == itemId)
                return (GroupItemBean) bean;
        }
        return null;
    }

    /**
     * 根据itemId获取child所在的group的position
     *
     * @param beans  整个数据list
     * @param itemId child的itemId
     * @return group的position
     */
    public static int getGroupPosition(List<DemoItemBean> beans, int itemId) {
        for (int i = 0; i < beans.size(); i++) {
            if (beans.get(i).getItemType() == DemoItemBean.TYPE_GROUP
                    && beans.get(i).getItemId() == itemId)
                return i;
        }
        return 0;
    }

    /**
     * new一个normal item数据
     *
     * @param itemId position
     * @return normal item数据
     */
    public static NormalItemBean newNormalItem(int itemId) {
        NormalItemBean bean = new NormalItemBean();
        bean.setItemId(itemId);
        bean.setChecked(false);
        bean.setTitle("Normal: " + itemId);
        bean.setItemType(DemoItemBean.TYPE_NORMAL);
        return bean;
    }

    public static GroupItemBean newGroupItem(int itemId) {
        List<ChildItemBean> childList = new ArrayList<>();
        GroupItemBean bean = new GroupItemBean();
        bean.setItemId(itemId);
        bean.setItemType(DemoItemBean.TYPE_GROUP);
        bean.setTitle("Group: " + itemId);
        bean.setChilds(childList);
        bean.setChecked(false);
        return bean;
    }

    public static ChildItemBean newChildItem(List<DemoItemBean> beans, int itemId, int childId) {
        GroupItemBean groupItemBean = getGroupBean(beans, itemId);
        ChildItemBean bean = new ChildItemBean();
        bean.setGroupId(itemId);
        bean.setItemId(itemId);
        bean.setItemType(DemoItemBean.TYPE_CHILD);
        bean.setTitle("group: " + itemId + " child: " + childId);
        bean.setChecked(false);
        if (groupItemBean != null)
            groupItemBean.getChilds().add(bean);
        return bean;
    }
}
public class MainActivity extends AppCompatActivity {

    private RecyclerView rvNestDemo;
    private DemoAdapter mDemoAdapter;
    private List<DemoItemBean> mDatas;
    private Button btnDelete, btnAddGroup, btnAddNormal, btnChild;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }

    private void initData() {
        mDatas = ParseHelper.getParseDatas();
    }

    private void initView() {
        rvNestDemo = (RecyclerView) findViewById(R.id.rv_nest_demo);
        btnDelete = (Button) findViewById(R.id.btn_delete);
        btnAddGroup = (Button) findViewById(R.id.btn_add_group);
        btnAddNormal = (Button) findViewById(R.id.btn_add_normal);
        btnChild = (Button) findViewById(R.id.btn_add_child);

        rvNestDemo.setLayoutManager(new LinearLayoutManager(this));
        mDemoAdapter = new DemoAdapter(this, mDatas);
        mDemoAdapter.setOnCheckChangeListener(new OnCheckChangeListener() {
            @Override
            public void onCheckedChanged(List<DemoItemBean> beans, int position, boolean
                    isChecked, int itemType) {
                switch (itemType) {
                    case DemoItemBean.TYPE_NORMAL:
                        normalCheckChange(beans, position, isChecked);
                        break;
                    case DemoItemBean.TYPE_GROUP:
                        groupCheckChange(beans, position, isChecked);
                        break;
                    case DemoItemBean.TYPE_CHILD:
                        childCheckChange(beans, position, isChecked);
                        break;
                }
            }
        });
        rvNestDemo.setAdapter(mDemoAdapter);

        btnDelete.setOnClickListener(new OnClickListener());
        btnAddGroup.setOnClickListener(new OnClickListener());
        btnAddNormal.setOnClickListener(new OnClickListener());
        btnChild.setOnClickListener(new OnClickListener());
    }

    /**
     * normal选中状态变化
     *
     * @param beans     数据
     * @param position  group position
     * @param isChecked 选中状态
     */
    private void normalCheckChange(List<DemoItemBean> beans, int position, boolean isChecked) {
        if (rvNestDemo.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
                && !rvNestDemo.isComputingLayout()) {//避免滑动时刷新数据
            beans.get(position).setChecked(isChecked);
        }
    }

    /**
     * group选中状态变化
     *
     * @param beans     数据
     * @param position  group position
     * @param isChecked 选中状态
     */
    private void groupCheckChange(List<DemoItemBean> beans, int position, boolean isChecked) {
        if (rvNestDemo.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
                && !rvNestDemo.isComputingLayout()) {//避免滑动时刷新数据
            beans.get(position).setChecked(isChecked);
            setChildCheck(beans, position, isChecked);
        }
    }

    /**
     * child选中状态变化
     *
     * @param beans     数据
     * @param position  child position
     * @param isChecked 选中状态
     */
    private void childCheckChange(List<DemoItemBean> beans, int position, boolean isChecked) {
        int itemId = beans.get(position).getItemId();

        if (rvNestDemo.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
                && !rvNestDemo.isComputingLayout()) {//避免滑动时刷新数据

            beans.get(position).setChecked(isChecked);

            GroupItemBean groupBean = ParseHelper.getGroupBean(beans, itemId);

            List<ChildItemBean> childList = ParseHelper.getChildList(beans, position);
            for (int i = 0; i < childList.size(); i++) {
                if (!childList.get(i).isChecked()) {//只要有一个child没有选中,group就不是选中
                    if (groupBean.isChecked() && !isChecked) {//group为选中状态
                        setGroupCheck(beans, itemId, false);
                        mDemoAdapter.notifyItemChanged(ParseHelper.getGroupPosition(beans,
                                itemId));
                    }
                    return;
                }
            }

            //child全部选中,group设置选中
            setGroupCheck(beans, itemId, true);
            mDemoAdapter.notifyItemChanged(ParseHelper.getGroupPosition(beans, itemId));
        }
    }

    /**
     * 一次设置group下所有child item选中状态
     *
     * @param beans     整个数据list
     * @param position  group position
     * @param isChecked 设置选中状态
     */
    private void setChildCheck(List<DemoItemBean> beans, int position, boolean isChecked) {
        for (int i = 0; i < beans.size(); i++) {
            //item id不相同直接跳过
            if (beans.get(i).getItemId() != beans.get(position).getItemId())
                continue;

            if (beans.get(i).getItemType() == DemoItemBean.TYPE_CHILD) {//让group下的所有child选中
                if (beans.get(i).isChecked() != isChecked) {
                    beans.get(i).setChecked(isChecked);
                    mDemoAdapter.notifyItemChanged(i);
                }
            }
        }
    }

    /**
     * 设置group item选中状态
     *
     * @param beans     整个数据list
     * @param itemId    child的itemId
     * @param isChecked 设置选中状态
     */
    private void setGroupCheck(List<DemoItemBean> beans, int itemId, boolean isChecked) {
        for (DemoItemBean bean : beans) {
            if (bean.getItemType() == DemoItemBean.TYPE_GROUP
                    && bean.getItemId() == itemId) {
                bean.setChecked(isChecked);
            }
        }
    }

    private class OnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_add_normal:
                    mDemoAdapter.addNormal();
                    rvNestDemo.smoothScrollToPosition(mDemoAdapter.getLastNormalItemPosition());
                    break;
                case R.id.btn_add_group:
                    mDemoAdapter.addGroup();
                    rvNestDemo.smoothScrollToPosition(mDemoAdapter.getLastItemPosition());
                    break;
                case R.id.btn_add_child:
                    mDemoAdapter.addChild();
                    rvNestDemo.smoothScrollToPosition(mDemoAdapter.getLastItemPosition());
                    break;
                case R.id.btn_delete:
                    mDemoAdapter.removeChecked();
                    break;
            }
        }
    }
}

写在最后:其实这篇文章并没有涉及到说一些很难的知识点,只是单纯的做一种实现思路的描述。天下程序千千万,实现的方法当然也各不相同。个人觉得,换一种思路看待问题,然后再解决这个问题,得到的收获远远要大于照本宣科的去解决问题。毕竟学习的目的是在解决问题的过程中拓宽思维,提升自己,而不仅仅是为了解决问题。

gitHub源码链接:https://github.com/Horrarndoo/NestingRecyclerViewDemo

上一篇下一篇

猜你喜欢

热点阅读