牛骨文教育服务平台(让学习变的简单)
博文笔记

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

创建时间:2014-11-21 投稿人: 浏览次数:8250

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


先上效果图:



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


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

/**
	* @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方法中回调即可:

/**
		 * @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:

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 + "
点击的是:"
						+ 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

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

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

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

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

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

<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

<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

<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

<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

<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


转载请注明出处:http://blog.csdn.net/shexiaoheng/article/details/41351247

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。