Android ExpandableListView双层嵌套实现三级树形菜单

2020-11-04 15:32:28 浏览数 (1)

在Android开发中,列表可以说是最常见的了,一般都是使用ListView,当涉及到二维数组时,更多的使用到ExpandableListView,然而当数据结构比较复杂时,就需要使用三级菜单或者更多级的菜单来显示,这就让人比较头疼了,最近做的项目就涉及到了三级菜单,遇到了不少问题,虽然不够完美,但是基本需求实现了,在此记录一下。(之前见过有人使用ListView实现4级、5级甚至更多级菜单的,是在Adapter的数据源里定义的结构,根据等级缩进左间距的倍数,链接地址找不到了,有兴趣的可以自己找找)

先上效果图:

简单介绍下重点,为了简便,把第一层ExpandableListView称之为EListOne,相应的Adapter称之为AdpOne;第二层ExpandableListView称之为EListTwo,相应的Adapter称之为AdpTwo。

首先第一个要处理的问题是在AdpOne的getChildView方法中,需要对EListTwo的高度进行动态计算,因为EListTwo展开和关闭时高度是不一样的,所以要在EListTwo的setOnGroupExpandListener和setOnGroupCollapseListener方法中做相应的处理:

代码语言:javascript复制
/**
 * @author Apathy、恒
 * 
 *   子ExpandableListView展开时,因为group只有一项,所以子ExpandableListView的总高度=
 *   (子ExpandableListView的child数量   1 )* 每一项的高度
 * */
 eListView.setOnGroupExpandListener(new OnGroupExpandListener() {
   @Override
   public void onGroupExpand(int groupPosition) {
 
 LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
      (child.getChildNames().size()   1)* (int) mContext.getResources().getDimension(R.dimen.parent_expandable_list_height));
 eListView.setLayoutParams(lp);
 }
 });
 
 /**
 * @author Apathy、恒
 * 
 * 子ExpandableListView关闭时,此时只剩下group这一项,所以子ExpandableListView的总高度即为一项的高度
 * */
 eListView.setOnGroupCollapseListener(new OnGroupCollapseListener() {
 @Override
 public void onGroupCollapse(int groupPosition) {
 
 LayoutParams lp = new LayoutParams(
  ViewGroup.LayoutParams.MATCH_PARENT, (int) mContext
  .getResources().getDimension(
   R.dimen.parent_expandable_list_height));
 eListView.setLayoutParams(lp);
 }
 });

只展示菜单肯定不是我们的最终需求,我们一般需要点击菜单后进行相应的界面跳转或者数据处理,所以就需要获取所点击的菜单精确下标,获取方法很简单,只需要定义一个接口,在AdpOne的getChildView方法中回调即可:

代码语言:javascript复制
/**
 * @author Apathy、恒
 * 
 *   点击子ExpandableListView子项时,调用回调接口
 * */
 eListView.setOnChildClickListener(new OnChildClickListener() {
 
 @Override
 public boolean onChildClick(ExpandableListView arg0, View arg1,
  int groupIndex, int childIndex, long arg4) {
 
 if (mTreeViewClickListener != null) {
 
  mTreeViewClickListener.onClickPosition(groupPosition,
  childPosition, childIndex);
 }
 return false;
 }
 });

下面是完整的代码:

MainActivity.java:

代码语言:javascript复制
package com.heng.tree;
import java.util.ArrayList;
import com.heng.tree.R;
import com.heng.tree.adapter.ParentAdapter;
import com.heng.tree.adapter.ParentAdapter.OnChildTreeViewClickListener;
import com.heng.tree.entity.ChildEntity;
import com.heng.tree.entity.ParentEntity;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnGroupExpandListener;
import android.widget.Toast;
/**
* 
* @author Apathy、恒
* 
* <br/ 
* 
* @email shexiaoheng@163.com
* 
* @blog
* <a href='http://blog.csdn.net/shexiaoheng' http://blog.csdn.net/shexiaoheng</a  
* 
* <br/ 
* <br/ 
* 
* @Detail 本Demo为ExpandableListView嵌套ExpandableListView实现三级菜单的例子
* 
* #ParentAdapter.OnChildTreeViewClickListener
* 
* */
public class MainActivity extends Activity implements OnGroupExpandListener,
OnChildTreeViewClickListener {
private Context mContext;
private ExpandableListView eList;
private ArrayList<ParentEntity  parents;
private ParentAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_main);
loadData();
initEList();
}
/**
* @author Apathy、恒
* 
*   初始化菜单数据源
* */
private void loadData() {
parents = new ArrayList<ParentEntity ();
for (int i = 0; i < 10; i  ) {
ParentEntity parent = new ParentEntity();
parent.setGroupName("父类父分组第"   i   "项");
parent.setGroupColor(getResources().getColor(
android.R.color.holo_red_light));
ArrayList<ChildEntity  childs = new ArrayList<ChildEntity ();
for (int j = 0; j < 8; j  ) {
ChildEntity child = new ChildEntity();
child.setGroupName("子类父分组第"   j   "项");
child.setGroupColor(Color.parseColor("#ff00ff"));
ArrayList<String  childNames = new ArrayList<String ();
ArrayList<Integer  childColors = new ArrayList<Integer ();
for (int k = 0; k < 5; k  ) {
childNames.add("子类第"   k   "项");
childColors.add(Color.parseColor("#ff00ff"));
}
child.setChildNames(childNames);
childs.add(child);
}
parent.setChilds(childs);
parents.add(parent);
}
}
/**
* @author Apathy、恒
* 
*   初始化ExpandableListView
* */
private void initEList() {
eList = (ExpandableListView) findViewById(R.id.eList);
eList.setOnGroupExpandListener(this);
adapter = new ParentAdapter(mContext, parents);
eList.setAdapter(adapter);
adapter.setOnChildTreeViewClickListener(this);
}
/**
* @author Apathy、恒
* 
*   点击子ExpandableListView的子项时,回调本方法,根据下标获取值来做相应的操作
* */
@Override
public void onClickPosition(int parentPosition, int groupPosition,
int childPosition) {
// do something
String childName = parents.get(parentPosition).getChilds()
.get(groupPosition).getChildNames().get(childPosition)
.toString();
Toast.makeText(
mContext,
"点击的下标为: parentPosition="   parentPosition
  " groupPosition="   groupPosition
  " childPosition="   childPosition   "n点击的是:"
  childName, Toast.LENGTH_SHORT).show();
}
/**
* @author Apathy、恒
* 
*   展开一项,关闭其他项,保证每次只能展开一项
* */
@Override
public void onGroupExpand(int groupPosition) {
for (int i = 0; i < parents.size(); i  ) {
if (i != groupPosition) {
eList.collapseGroup(i);
}
}
}
}

ParentAdapter.java

代码语言:javascript复制
package com.heng.tree.adapter;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupCollapseListener;
import android.widget.ExpandableListView.OnGroupExpandListener;
import android.widget.AbsListView.LayoutParams;
import android.widget.TextView;
import com.heng.tree.R;
import com.heng.tree.entity.ChildEntity;
import com.heng.tree.entity.ParentEntity;
/**
* 
* @author Apathy、恒
* 
*   父类分组的适配器
* 
* <br/ 
* <br/ 
* 
*   方法 {@link #getChildView(int, int, boolean, View, ViewGroup)}<b <font
*   color='#ff00ff' size='2' 极其重要</font </b 
* 
* */
public class ParentAdapter extends BaseExpandableListAdapter {
private Context mContext;// 上下文
private ArrayList<ParentEntity  mParents;// 数据源
private OnChildTreeViewClickListener mTreeViewClickListener;// 点击子ExpandableListView子项的监听
public ParentAdapter(Context context, ArrayList<ParentEntity  parents) {
this.mContext = context;
this.mParents = parents;
}
@Override
public ChildEntity getChild(int groupPosition, int childPosition) {
return mParents.get(groupPosition).getChilds().get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition) {
return mParents.get(groupPosition).getChilds() != null ? mParents
.get(groupPosition).getChilds().size() : 0;
}
@Override
public View getChildView(final int groupPosition, final int childPosition,
boolean isExpanded, View convertView, ViewGroup parent) {
final ExpandableListView eListView = getExpandableListView();
ArrayList<ChildEntity  childs = new ArrayList<ChildEntity ();
final ChildEntity child = getChild(groupPosition, childPosition);
childs.add(child);
final ChildAdapter childAdapter = new ChildAdapter(this.mContext,
childs);
eListView.setAdapter(childAdapter);
/**
* @author Apathy、恒
* 
*   点击子ExpandableListView子项时,调用回调接口
* */
eListView.setOnChildClickListener(new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView arg0, View arg1,
int groupIndex, int childIndex, long arg4) {
if (mTreeViewClickListener != null) {
mTreeViewClickListener.onClickPosition(groupPosition,
childPosition, childIndex);
}
return false;
}
});
/**
* @author Apathy、恒
* 
*   子ExpandableListView展开时,因为group只有一项,所以子ExpandableListView的总高度=
*   (子ExpandableListView的child数量   1 )* 每一项的高度
* */
eListView.setOnGroupExpandListener(new OnGroupExpandListener() {
@Override
public void onGroupExpand(int groupPosition) {
LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, (child
.getChildNames().size()   1)
* (int) mContext.getResources().getDimension(
R.dimen.parent_expandable_list_height));
eListView.setLayoutParams(lp);
}
});
/**
* @author Apathy、恒
* 
*   子ExpandableListView关闭时,此时只剩下group这一项,
*   所以子ExpandableListView的总高度即为一项的高度
* */
eListView.setOnGroupCollapseListener(new OnGroupCollapseListener() {
@Override
public void onGroupCollapse(int groupPosition) {
LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, (int) mContext
.getResources().getDimension(
R.dimen.parent_expandable_list_height));
eListView.setLayoutParams(lp);
}
});
return eListView;
}
/**
* @author Apathy、恒
* 
*   动态创建子ExpandableListView
* */
public ExpandableListView getExpandableListView() {
ExpandableListView mExpandableListView = new ExpandableListView(
mContext);
LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, (int) mContext
.getResources().getDimension(
R.dimen.parent_expandable_list_height));
mExpandableListView.setLayoutParams(lp);
mExpandableListView.setDividerHeight(0);// 取消group项的分割线
mExpandableListView.setChildDivider(null);// 取消child项的分割线
mExpandableListView.setGroupIndicator(null);// 取消展开折叠的指示图标
return mExpandableListView;
}
@Override
public Object getGroup(int groupPosition) {
return mParents.get(groupPosition);
}
@Override
public int getGroupCount() {
return mParents != null ? mParents.size() : 0;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
GroupHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.parent_group_item, null);
holder = new GroupHolder(convertView);
convertView.setTag(holder);
} else {
holder = (GroupHolder) convertView.getTag();
}
holder.update(mParents.get(groupPosition));
return convertView;
}
/**
* @author Apathy、恒
* 
*   Holder优化
* */
class GroupHolder {
private TextView parentGroupTV;
public GroupHolder(View v) {
parentGroupTV = (TextView) v.findViewById(R.id.parentGroupTV);
}
public void update(ParentEntity model) {
parentGroupTV.setText(model.getGroupName());
parentGroupTV.setTextColor(model.getGroupColor());
}
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
/**
* @author Apathy、恒
* 
*   设置点击子ExpandableListView子项的监听
* */
public void setOnChildTreeViewClickListener(
OnChildTreeViewClickListener treeViewClickListener) {
this.mTreeViewClickListener = treeViewClickListener;
}
/**
* @author Apathy、恒
* 
*   点击子ExpandableListView子项的回调接口
* */
public interface OnChildTreeViewClickListener {
void onClickPosition(int parentPosition, int groupPosition,
int childPosition);
}
}

ChildAdapter.java

代码语言:javascript复制
package com.heng.tree.adapter;
import java.util.ArrayList;
import com.heng.tree.R;
import com.heng.tree.entity.ChildEntity;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
/**
* 
* @author Apathy、恒
* 
* <br/ 
* <br/ 
* 
*   子类分组的适配器
* 
* <br/ 
* <br/ 
* 
*   方法{@link #isChildSelectable(int,int)} <b <font color='#ff00ff'
*   size='2' 必须返回true</font </b 
* 
* */
public class ChildAdapter extends BaseExpandableListAdapter {
private Context mContext;// 上下文
private ArrayList<ChildEntity  mChilds;// 数据源
public ChildAdapter(Context context, ArrayList<ChildEntity  childs) {
this.mContext = context;
this.mChilds = childs;
}
@Override
public int getChildrenCount(int groupPosition) {
return mChilds.get(groupPosition).getChildNames() != null ? mChilds
.get(groupPosition).getChildNames().size() : 0;
}
@Override
public String getChild(int groupPosition, int childPosition) {
if (mChilds.get(groupPosition).getChildNames() != null
&& mChilds.get(groupPosition).getChildNames().size()   0)
return mChilds.get(groupPosition).getChildNames()
.get(childPosition).toString();
return null;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isExpanded, View convertView, ViewGroup parent) {
ChildHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.child_child_item, null);
holder = new ChildHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ChildHolder) convertView.getTag();
}
holder.update(getChild(groupPosition, childPosition));
return convertView;
}
/**
* @author Apathy、恒
* 
*   Holder优化
* */
class ChildHolder {
private TextView childChildTV;
public ChildHolder(View v) {
childChildTV = (TextView) v.findViewById(R.id.childChildTV);
}
public void update(String str) {
childChildTV.setText(str);
childChildTV.setTextColor(Color.parseColor("#00ffff"));
}
}
@Override
public Object getGroup(int groupPosition) {
if (mChilds != null && mChilds.size()   0)
return mChilds.get(groupPosition);
return null;
}
@Override
public int getGroupCount() {
return mChilds != null ? mChilds.size() : 0;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
GroupHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.child_group_item, null);
holder = new GroupHolder(convertView);
convertView.setTag(holder);
} else {
holder = (GroupHolder) convertView.getTag();
}
holder.update(mChilds.get(groupPosition));
return convertView;
}
/**
* @author Apathy、恒
* 
*   Holder优化
* */
class GroupHolder {
private TextView childGroupTV;
public GroupHolder(View v) {
childGroupTV = (TextView) v.findViewById(R.id.childGroupTV);
}
public void update(ChildEntity model) {
childGroupTV.setText(model.getGroupName());
childGroupTV.setTextColor(model.getGroupColor());
}
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
/**
* ==============================================
* 此处必须返回true,否则无法响应子项的点击事件===============
* ==============================================
**/
return true;
}
}

CListAdapter.java

代码语言:javascript复制
package com.heng.tree.adapter;
import java.util.ArrayList;
import com.heng.tree.R;
import com.heng.tree.entity.ChildEntity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
/**
* 
* @author Apathy、恒
* 
*   子类子类列表的适配器
* 
* */
public class CListAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<ChildEntity  mChilds;
public CListAdapter(Context context, ArrayList<ChildEntity  childs) {
this.mContext = context;
this.mChilds = childs;
}
@Override
public int getCount() {
return mChilds != null ? mChilds.size() : 0;
}
@Override
public Object getItem(int position) {
if ((getCount()   0) && (position   0 && position < mChilds.size())) {
return mChilds.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.child_child_item, null);
holder = new Holder(convertView);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.update(mChilds.get(position).getGroupName());
return convertView;
}
class Holder {
private TextView tv;
public Holder(View v) {
tv = (TextView) v.findViewById(R.id.childChildTV);
}
public void update(String text) {
tv.setText(text);
}
}
}

ParentEntity.java

代码语言:javascript复制
package com.heng.tree.entity;
import java.util.ArrayList;
/**
* 
* @author Apathy、恒
* 
*   子类分组的实体
* 
* */
public class ParentEntity {
private int groupColor;
private String groupName;
private ArrayList<ChildEntity  childs;
/* ==========================================================
* ======================= get method =======================
* ========================================================== */
public int getGroupColor() {
return groupColor;
}
public String getGroupName() {
return groupName;
}
public ArrayList<ChildEntity  getChilds() {
return childs;
}
/* ==========================================================
* ======================= set method =======================
* ========================================================== */
public void setGroupColor(int groupColor) {
this.groupColor = groupColor;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setChilds(ArrayList<ChildEntity  childs) {
this.childs = childs;
}
}

ChildEntity.java

代码语言:javascript复制
package com.heng.tree.entity;
import java.util.ArrayList;
/**
* 
* @author Apathy、恒
* 
*   父类分组的实体
* 
* */
public class ChildEntity {
private int groupColor;
private String groupName;
private ArrayList<String  childNames;
/* ==========================================================
* ======================= get method =======================
* ========================================================== */
public int getGroupColor() {
return groupColor;
}
public String getGroupName() {
return groupName;
}
public ArrayList<String  getChildNames() {
return childNames;
}
/* ==========================================================
* ======================= set method =======================
* ========================================================== */
public void setGroupColor(int groupColor) {
this.groupColor = groupColor;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setChildNames(ArrayList<String  childNames) {
this.childNames = childNames;
}
}

activity_main.xml

代码语言:javascript复制
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"  
<ExpandableListView
android:id="@ id/eList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:groupIndicator="@null" / 
</FrameLayout 

parent_group_item.xml

代码语言:javascript复制
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/parent_expandable_list_group_padding_left"  
<TextView
android:id="@ id/parentGroupTV"
android:layout_width="wrap_content"
android:layout_height="@dimen/parent_expandable_list_height"
android:gravity="center_vertical" / 
</RelativeLayout 

child_group_item.xml

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"  
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/parent_expandable_list_height"  
<TextView
android:id="@ id/childGroupTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/child_expandable_list_group_padding_left" / 
</RelativeLayout 
</LinearLayout 

child_child_item.xml

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"  
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/parent_expandable_list_height"  
<TextView
android:id="@ id/childChildTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/child_expandable_list_child_padding_left" / 
</RelativeLayout 
</LinearLayout 

dimens.xml

代码语言:javascript复制
<resources 
<!-- Default screen margins, per the Android Design guidelines. -- 
<dimen name="parent_expandable_list_height" 50dp</dimen 
<dimen name="parent_expandable_list_group_padding_left" 10dp</dimen 
<dimen name="child_expandable_list_group_padding_left" 40dp</dimen 
<dimen name="child_expandable_list_child_padding_left" 75dp</dimen 
</resources 

点此下载demo

以上就是本文的全部内容,希望对大家的学习有所帮助。

0 人点赞