其實(shí)在實(shí)際開發(fā)中惧盹,難免會遇到一些類似于需要listview嵌套listview或者RecyclerView嵌套RecyclerView的界面需要實(shí)現(xiàn)事秀。作為開發(fā)人員的我們惠呼,當(dāng)然希望這種需求越少越好滋早,但是如果偏偏就是有這種需求恳守,用哪一種方式去實(shí)現(xiàn)比較好呢考婴?
首先看一個(gè)很變態(tài)的界面效果圖,估計(jì)很多人看到這個(gè)界面的第一眼就是懵逼的催烘,這尼瑪什么玩意沥阱?
其實(shí)倒不是說實(shí)現(xiàn)圖中的這種效果有多難,而是這種類似于嵌套的界面伊群,實(shí)現(xiàn)的最終效果往往會達(dá)不到預(yù)期考杉。
要實(shí)現(xiàn)這么個(gè)界面,根據(jù)網(wǎng)上大部分資料來說舰始,無非就是下面這2種方法:
- 使用addView到布局的方式將item添加進(jìn)布局容器中崇棠;
- RecyclerView嵌套RecyclerView(ListView嵌套ListView);
使用addView到布局的方式將item添加進(jìn)布局容器中
其實(shí)這種方式是一種比較好的實(shí)現(xiàn)方式丸卷,使用這種方式實(shí)現(xiàn)的話枕稀,實(shí)際上是將item分為兩類,一類是Normal item谜嫉,一類是Group item抽莱。在Group item中添加一個(gè)空布局作為容器,然后根據(jù)child item的數(shù)量在adapter中使用addView的方式將child item添加到Group item中骄恶。但是這種方法也有一個(gè)弊端食铐,就是child item數(shù)據(jù)刷新的時(shí)候,需要先將容器中的child全部清除僧鲁,然后再重新添加新的child虐呻,這種就會導(dǎo)致數(shù)據(jù)刷新的時(shí)候界面明顯有一個(gè)先清空再添加的界面變化象泵,對用戶體驗(yàn)上來講是很不友好的。
RecyclerView嵌套RecyclerView(ListView嵌套ListView)
ListView嵌套ListView網(wǎng)上有很多相關(guān)的資料斟叼,這種方式首先存在的問題就是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就可以了忽孽。雖然這樣實(shí)現(xiàn)看起來很簡單,但是不論是ListView嵌套ListView也好谢床,RecyclerView嵌套RecyclerView也好兄一,滑動的時(shí)候,由于要計(jì)算child的高度识腿,最直觀的體現(xiàn)就是滑動卡頓出革。然后還有一個(gè)問題就是這種嵌套的方式item的viewHolder無法復(fù)用,數(shù)據(jù)量大到一定程度就OOM了渡讼。
單RecyclerView實(shí)現(xiàn)
既然上面兩種方式都有弊端骂束,而且相對比較影響體驗(yàn)。我們不妨換一種思路來實(shí)現(xiàn)這種布局:使用單RecyclerView實(shí)現(xiàn)成箫。
前面兩種方式針對的是控件展箱,現(xiàn)在既然要使用單RecyclerView實(shí)現(xiàn),那么我們的做法就是從數(shù)據(jù)層面做修改蹬昌,也就是不論Normal item析藕、Group item、Child item凳厢,都只是作為一個(gè)RecyclerView的一種布局來實(shí)現(xiàn)账胧,而不是將child作為Group item的子布局來實(shí)現(xiàn)。
首先看看模擬的實(shí)體類:
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;
}
}
假設(shè)這3個(gè)實(shí)體類都是服務(wù)器返回的數(shù)據(jù)先紫,根據(jù)我們的思路治泥,Group、Child遮精、Normal都是只是RecyclerView的不同類型的item而已居夹,所以這里對實(shí)體類做一下調(diào)整,調(diào)整后的實(shí)體類如下:
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;
}
}
接下來寫模擬數(shù)據(jù)的處理本冲,將Normal准脂、Group、Child整合成一個(gè)list檬洞。
public class ParseHelper {
//=========== 這里模擬服務(wù)器返回的數(shù)據(jù) ==========
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對應(yīng)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);
}
}
}
三個(gè)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數(shù)據(jù)
* @param bean item數(shù)據(jù)
*/
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數(shù)據(jù)
* @param bean item數(shù)據(jù)
*/
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數(shù)據(jù)
*
* @param bean item數(shù)據(jù)
*/
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實(shí)現(xiàn)嵌套布局就實(shí)現(xiàn)了,其實(shí)整體實(shí)現(xiàn)起來還是挺簡單的添怔,child通過itemId和對應(yīng)的Group綁定湾戳,具體的item類型根據(jù)itemType來區(qū)分贤旷。
然后增加一些好玩的功能,包括選中item砾脑、添加item以及刪除選中item等幼驶,具體效果如下圖。
其實(shí)這些功能都是一個(gè)購物車界面比較常見的功能韧衣,只是剛好在這里順帶實(shí)現(xiàn)了盅藻。實(shí)現(xiàn)起來還是沒什么特別好說的,直接貼上一些關(guān)鍵代碼吧畅铭。
public class DemoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
……
/**
* 刪除選中item
*/
public void removeChecked() {
int iMax = mDatas.size() - 1;
//這里要倒序氏淑,因?yàn)橐獎h除mDatas中的數(shù)據(jù),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()) {//如果列表中沒有g(shù)roup顶瞒,直接在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);//通知數(shù)據(jù)與界面重新綁定
}
/**
* 添加 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);//通知數(shù)據(jù)與界面重新綁定
}
/**
* 添加 Child item
* <p>
* child item添加位置永遠(yuǎn)歸屬于最后一個(gè)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);//通知數(shù)據(jù)與界面重新綁定
if (onCheckChangeListener != null)
onCheckChangeListener.onCheckedChanged(mDatas, addPosition, mDatas.get(addPosition)
.isChecked(), DemoItemBean.TYPE_CHILD);
}
/**
* 當(dāng)前l(fā)ist是否含有g(shù)roup
*
* @return 當(dāng)前l(fā)ist是否含有g(shù)roup
*/
private boolean isHaveGroup() {
boolean isHaveGroup = false;//當(dāng)前列表是否包含group
for (int i = 0; i < mDatas.size(); i++) {
if (mDatas.get(i).getItemType() == DemoItemBean.TYPE_GROUP) {
isHaveGroup = true;
break;
}
}
return isHaveGroup;
}
/**
* 獲取最后一個(gè)Normal item的position
*
* @return 最后一個(gè)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;
}
/**
* 獲取最后一個(gè)item的position
*
* @return 最后一個(gè)item的position
*/
public int getLastItemPosition() {
return mDatas.size();
}
}
public class ParseHelper {
……
/**
* 獲取group下的child list
*
* @param beans 整個(gè)數(shù)據(jù)list
* @param position 當(dāng)前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;
}
/**
* 根據(jù)itemId獲取child所在的group的position
*
* @param beans 整個(gè)數(shù)據(jù)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一個(gè)normal item數(shù)據(jù)
*
* @param itemId position
* @return normal item數(shù)據(jù)
*/
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選中狀態(tài)變化
*
* @param beans 數(shù)據(jù)
* @param position group position
* @param isChecked 選中狀態(tài)
*/
private void normalCheckChange(List<DemoItemBean> beans, int position, boolean isChecked) {
if (rvNestDemo.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
&& !rvNestDemo.isComputingLayout()) {//避免滑動時(shí)刷新數(shù)據(jù)
beans.get(position).setChecked(isChecked);
}
}
/**
* group選中狀態(tài)變化
*
* @param beans 數(shù)據(jù)
* @param position group position
* @param isChecked 選中狀態(tài)
*/
private void groupCheckChange(List<DemoItemBean> beans, int position, boolean isChecked) {
if (rvNestDemo.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
&& !rvNestDemo.isComputingLayout()) {//避免滑動時(shí)刷新數(shù)據(jù)
beans.get(position).setChecked(isChecked);
setChildCheck(beans, position, isChecked);
}
}
/**
* child選中狀態(tài)變化
*
* @param beans 數(shù)據(jù)
* @param position child position
* @param isChecked 選中狀態(tài)
*/
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()) {//避免滑動時(shí)刷新數(shù)據(jù)
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()) {//只要有一個(gè)child沒有選中夸政,group就不是選中
if (groupBean.isChecked() && !isChecked) {//group為選中狀態(tài)
setGroupCheck(beans, itemId, false);
mDemoAdapter.notifyItemChanged(ParseHelper.getGroupPosition(beans,
itemId));
}
return;
}
}
//child全部選中元旬,group設(shè)置選中
setGroupCheck(beans, itemId, true);
mDemoAdapter.notifyItemChanged(ParseHelper.getGroupPosition(beans, itemId));
}
}
/**
* 一次設(shè)置group下所有child item選中狀態(tài)
*
* @param beans 整個(gè)數(shù)據(jù)list
* @param position group position
* @param isChecked 設(shè)置選中狀態(tài)
*/
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);
}
}
}
}
/**
* 設(shè)置group item選中狀態(tài)
*
* @param beans 整個(gè)數(shù)據(jù)list
* @param itemId child的itemId
* @param isChecked 設(shè)置選中狀態(tài)
*/
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;
}
}
}
}
寫在最后:其實(shí)這篇文章并沒有涉及到說一些很難的知識點(diǎn)榴徐,只是單純的做一種實(shí)現(xiàn)思路的描述。天下程序千千萬匀归,實(shí)現(xiàn)的方法當(dāng)然也各不相同坑资。個(gè)人覺得,換一種思路看待問題穆端,然后再解決這個(gè)問題袱贮,得到的收獲遠(yuǎn)遠(yuǎn)要大于照本宣科的去解決問題。畢竟學(xué)習(xí)的目的是在解決問題的過程中拓寬思維体啰,提升自己攒巍,而不僅僅是為了解決問題。
gitHub源碼鏈接:https://github.com/Horrarndoo/NestingRecyclerViewDemo