可展开的列表组件——ExpandableListView深入解析
一、知识点
1、ExpandableListView常用XML属性
2.ExpandableListView继承BaseExpandableListAdapter后重写的各个函数详解
3.ExpandableListView自定义下拉图标
二、解析
2.1、ExpandableListView常用XML属性
2.1.1 设置点击item项后该item项的背景色
//当你选中某一个item项时,该item项的背景会变色,下面的值是将该背景色设置为透明
android:listSelector="#00000000"
2.1.2 设置item项的高度
//这个是用在item的布局文件里
android:minHeight="50dp"
2.1.3 拖动时背景图片问题
//在拖动的时候背景图片消失变成黑色背景,等到拖动完毕我们自己的背景图片才显示出来
android:scrollingCache=”false”
或 android:cacheColorHint=”#00000000″
2.1.4 分割线高度
android:dividerHeight="1dp"
2.2ExpandableListView继承BaseExpandableListAdapter后重写的各个函数详解
下面的代码几乎每条都给了注释,就不再赘述了。
2.2.1 child.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal" > <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" android:textSize="18dp" android:text="this" /> </LinearLayout>
2.2.2 group.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="50dp" android:orientation="horizontal" > <ImageView android:layout_marginLeft="10dp" android:layout_gravity="center_vertical" android:id="@+id/arrow" android:layout_width="25dp" android:layout_height="25dp" /> <TextView android:layout_marginLeft="10dp" android:id="@+id/logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20dp" android:layout_gravity="center_vertical" /> </LinearLayout>
2.2.3 colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="gray">#BEBEBE</color> <color name="SeaGreen1">#54FF9F</color> </resources>
2.2.4 activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ExpandableListView android:id="@+id/expandlist" android:divider="@color/gray" android:childDivider="@color/SeaGreen1" android:layout_width="match_parent" android:layout_height="match_parent" android:groupIndicator="@null" android:listSelector="#00000000" android:dividerHeight="1dp" > </ExpandableListView> </LinearLayout>
2.2.5 MyExpandableListAdapter.java
package com.yds.example; import java.util.List; import java.util.Map; import android.content.Context; import android.database.DataSetObserver; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyExpandableListAdapter extends BaseExpandableListAdapter { //上下文 Context context; //声明一个布局管理器对象 LayoutInflater inflater; //声明一个组集合 List<Map<String, Object>>group; //声明一个子元素集合 List<List<Map<String, Object>>>child; //声明一个map对象 Map<String, Object>map; /** * 自定义适配器的构造函数 * @param context 上下文 * @param group 组集合 * @param child 子元素集合 */ public MyExpandableListAdapter(Context context,List<Map<String, Object>>group,List<List<Map<String, Object>>>child){ //初始化上下文 this.context = context; //初始化布局管理器对象 inflater = LayoutInflater.from(context); //初始化组集合 this.group = group; //初始化子元素集合 this.child = child; } /** * ExpandableListAdapter里面的所有条目 * 都可用吗?如果是yes,就意味着所有条目可以选择和点击了。 * 返回值:返回True表示所有条目均可用。 */ @Override public boolean areAllItemsEnabled() { // TODO Auto-generated method stub return true; } /** * 获取指定组中的指定子元素数据。 */ @Override public Object getChild(int groupPosition, int childPosition) { // TODO Auto-generated method stub /*child.get(groupPosition)是得到groupPosition处的list对象,然后 * child.get(groupPosition).get(childPosition)得到child的map对象,然后 * child.get(groupPosition).get(childPosition).get("Child")是得到key值 * 为Child的值 * */ return child.get(groupPosition).get(childPosition).get("Child"); } /** * 获取指定组中的指定子元素ID,这个ID在组里一定是唯一的。联合ID(getCombinedChildId(long, long))在所有条目(所有组和所有元素)中也是唯一的。 */ @Override public long getChildId(int groupPosition, int childPosition) { // TODO Auto-generated method stub /******子元素的位置********/ return childPosition; } /**获取一个视图对象,显示指定组中的指定子元素数据。 * @param groupPosition 组位置(该组内部含有子元素) * @param childPosition 子元素位置(决定返回哪个视图) * @param isLastChild 子元素是否处于组中的最后一个 * @param convertView 重用已经有的视图对象,它是RecycleBin缓存机制调用getScrapView方法获取废弃已缓存的view. * @param parent 返回的视图(View)对象始终依附于的视图组。通俗的说是它的父视图。 */ @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolderChild viewHolderChild; /*当convertView为空,也就是没有废弃已缓存 view时,将执行下面方法,调用layoutinflate的 * inflate()方法来加载一个view。 * 如有不懂,请点击:http://blog.csdn.net/libmill/article/details/49644743 */ if(convertView==null){ //重新加载布局 convertView = inflater.inflate(R.layout.child, null); //初始化控件管理器(自己命名的) viewHolderChild = new ViewHolderChild(); //绑定控件id viewHolderChild.tv = (TextView) convertView.findViewById(R.id.tv); /*convertView的setTag将viewHolderChild设置到Tag中,以便系统第二次绘制 ExpandableListView时从Tag中取出 */ convertView.setTag(viewHolderChild); }else{ //当convertView不为空时,从Tag中取出viewHolderChild viewHolderChild = (ViewHolderChild) convertView.getTag(); } //给子元素的TextView设置值 viewHolderChild.tv.setText(getChild(groupPosition, childPosition).toString()); //返回视图对象,这里是childPostion处的视图 return convertView; } /** * 获取指定组中子元素的个数 */ @Override public int getChildrenCount(int groupPosition) { // TODO Auto-generated method stub return child.get(groupPosition).size(); } /** * 从列表所有项(组或子项)中获得一个唯一的子ID号。可折叠列表要求每个元素(组或子项)在所有的子元素和组中 * 有一个唯一的ID。本方法负责根据所给的子ID号和组ID号返回唯一的ID。此外,若hasStableIds() * 是true,那么必须要返回稳定的ID。 * @param groupId 包含该子元素的组ID * @param childId 子元素的ID * @return:列表所有项(组或子项)中唯一的(和可能稳定)的子元素ID号。(译者注:ID理论上是稳定的, * 不会发生冲突的情况。也就是说,这个列表会有组、子元素,它们的ID都是唯一的。) */ @Override public long getCombinedChildId(long groupId , long childId ) { // TODO Auto-generated method stub return 0; } /** * 获取组ID * @param groupId 组ID * @return :组ID */ @Override public long getCombinedGroupId(long groupId) { // TODO Auto-generated method stub return 0; } /** * 得到指定组的组数据 * @param groupPosition:指定的组的位置 * @return 返回指定组的组数据 */ @Override public Object getGroup(int groupPosition) { // TODO Auto-generated method stub /**group.get(groupPosition)获取map对象 * group.get(groupPosition).get("Group")获取key值为Group的数据 * **/ return group.get(groupPosition).get("Group"); } /** * 获取组长 */ @Override public int getGroupCount() { // TODO Auto-generated method stub return group.size(); } /** * 获取指定组的Id */ @Override public long getGroupId(int groupPosition) { // TODO Auto-generated method stub return groupPosition; } /** * 获取指定组的视图对象 * @param groupPosition:组位置(决定返回哪个视图) * @param isExpanded:改组是展开状态还是伸缩状态 * @param convertView:重用已有的视图对象 * @return 返回指定组的视图对象 */ @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolderGroup viewHolder; //判断convertView是否为空,convertView是RecycleBean调用getScrapView函数得到废弃已缓存的view if(convertView==null){ //初始化控件管理器对象 viewHolder = new ViewHolderGroup(); //重新加载布局 convertView = inflater.inflate(R.layout.group, null); //给组元素绑定ID viewHolder.logo_tv = (TextView) convertView.findViewById(R.id.logo); //给组元素箭头绑定ID viewHolder.arrow = (ImageView) convertView.findViewById(R.id.arrow); //convertView将viewHolder设置到Tag中,以便再次绘制ExpandableListView时从Tag中取出viewHolder; convertView.setTag(viewHolder); }else {//如果convertView不为空,即getScrapView得到废弃已缓存的view //从Tag中取出之前存入的viewHolder viewHolder = (ViewHolderGroup) convertView.getTag(); } //设置组值 viewHolder.logo_tv.setText(getGroup(groupPosition).toString()); //如果组是展开状态 if (isExpanded) { //箭头向下 viewHolder.arrow.setImageResource(R.drawable.arrow_down); }else{//如果组是伸缩状态 //箭头向右 viewHolder.arrow.setImageResource(R.drawable.arrow); } //返回得到的指定组的视图对象 return convertView; } /** * 组和子元素是否持有稳定的ID,也就是底层数据的改变不会影响到它们。 * @return 返回一个Boolean类型的值,如果为TRUE,意味着相同的ID永远引用相同的对象 */ @Override public boolean hasStableIds() { // TODO Auto-generated method stub return false; } /** * 是否选中指定位置上的子元素。 */ @Override public boolean isChildSelectable(int arg0, int arg1) { // TODO Auto-generated method stub return true; } /** * 如果当前适配器不包含任何数据则返回True。经常用来决定一个空视图是否应该被显示。 * 一个典型的实现将返回表达式getCount() == 0的结果,但是由于getCount()包含了头部和尾部,适配器可能需要不同的行为。 */ @Override public boolean isEmpty() { // TODO Auto-generated method stub return false; } /** * 当组收缩状态的时候此方法被调用。 */ @Override public void onGroupCollapsed(int groupPosition) { // TODO Auto-generated method stub } /** * 当组展开状态的时候此方法被调用。 */ @Override public void onGroupExpanded(int groupPosition) { // TODO Auto-generated method stub } /** * 注册一个观察者(observer),当此适配器数据修改时即调用此观察者。 * @param observer:当数据修改时通知调用的对象 */ @Override public void registerDataSetObserver(DataSetObserver observer) { // TODO Auto-generated method stub } /** * 取消先前通过registerDataSetObserver(DataSetObserver)方式注册进该适配器中的观察者对象。 * @param observer 取消这个观察者的注册 */ @Override public void unregisterDataSetObserver(DataSetObserver observer) { // TODO Auto-generated method stub } /** * 组控件管理器 * @author Administrator * */ class ViewHolderGroup{ TextView logo_tv; ImageView arrow; } /** * 子控件管理器 * @author Administrator * */ class ViewHolderChild{ TextView tv; } }
2.2.6 MainActivity.java
package com.yds.example; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.os.Bundle; import android.widget.ExpandableListView; public class MainActivity extends Activity { //声明一个可伸展的列表视图对象 private ExpandableListView expandlist; //声明并初始化一个组集合对象,该集合时一个一维数组 private List<Map<String, Object>>groupList = new ArrayList<Map<String,Object>>(); //声明一个子元素集合对象,该集合是一个数组链表 private List<List<Map<String, Object>>>childList = new ArrayList<List<Map<String,Object>>>(); //声明一个子元素集合对象 private List<Map<String, Object>>child; //声明一个map对象 private Map<String, Object>map; //组元素值 private String[] armTypes = new String[]{ "WORD", "EXCEL", "EMAIL", "PPT" }; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); //加载布局 setContentView(R.layout.activity_main); //给组赋值 for (int i = 0; i < armTypes.length; i++) { //每次都要初始化map对象 map = new HashMap<String, Object>(); //将组值放入key为Group的map中 map.put("Group", armTypes[i]); //将map添加到组集合中 groupList.add(map); } //给每组的子元素赋值 for (int j = 0; j < 4; j++) { //每次初始化子集合对象。该对象是一个一维数组 child = new ArrayList<Map<String,Object>>(); //每个组下面有25个子元素 for (int i = 0; i < 25; i++) { //初始化map对象 map = new HashMap<String, Object>(); //将子元素的值放入key值为Child的map中 map.put("Child", "this is "+i+" example"); //将map添加到一维数组中 child.add(map); } //将一维数组添加到集合中 childList.add(child); } //可伸展的列表视图绑定ID expandlist = (ExpandableListView) findViewById(R.id.expandlist); //声明并初始化一个adapter MyExpandableListAdapter adapter = new MyExpandableListAdapter(MainActivity.this,groupList,childList); //可伸展的列表视图加载adapter expandlist.setAdapter(adapter); } }
三、结果截图: