首页 > 代码库 > android中用ExpandableListView实现三级扩展列表

android中用ExpandableListView实现三级扩展列表

 工作中遇到一个选择车型的问题,需要在扩展列表中选择车辆品牌->车型->年款,所以必须得使用三级的扩展列表来实现,而且第三级还得使用GridView来展示。下面就一步步来吧。


       1.定义需要使用的车型类,每个车辆品牌下面包含n个车型,每个车型下面包含n个年款

点击(此处)折叠或打开

  1. /**

  2.  * 汽车的品牌类

  3.  * 

  4.  * @author liyanshun 2014-2-21

  5.  */

  6. public class CarBrand {

  7.     /**

  8.      * 汽车的品牌名字

  9.      */

  10.     public String mBrandName;

  11.     public String mSortLetters;

  12.     /** 该品牌下包含的汽车类型 */

  13.     public ArrayList<CarStyle> mCarStyleList;

  14. }


  15. /**

  16.  * 汽车车类型

  17.  * 

  18.  * @author liyanshun 2014-2-26

  19.  */

  20. public class CarStyle {

  21.     /** 汽车的类型名 */

  22.     public String mStyleName;

  23.     /** 该类型下包含的年款 */

  24.     public ArrayList<String> mModelList;

  25. }


      2.列表要可以按照首字母快速定位,这个功能参考了别人的实现,详情见这篇博客:http://blog.csdn.net/xiaanming/article/details/12684155

      3.实现第一级扩展列表。第一级比较好实现,只用使用一个ExpandableListView,并定义Adapter就可以了。需要注意的是每个品牌下面的子View个数即为其包含的车型个数,而每个车型都是一个新                                   的ExpandableListView。当然也可以使用一个ExpandableListView来展示所有的车型,但是那样的话会导致点击靠下的车型时,其后面的车型可能会被后一项的品牌给覆盖掉。所以在这里使用多了多                                个ExpandableListView。Adapter的实现如下:


点击(此处)折叠或打开

  1. /**

  2.  * 用来选择车辆品牌并且带字母排序的adapter

  3.  * 

  4.  * @author liyanshun 2014-2-21

  5.  */

  6. public class CarBrandAdapter extends BaseExpandableListAdapter implements

  7.         SectionIndexer {

  8.     private List<CarBrand> mBrandList = null;

  9.     private Context mContext;


  10.     public CarBrandAdapter(Context mContext, List<CarBrand> list) {

  11.         this.mContext = mContext;

  12.         this.mBrandList = list;

  13.     }


  14.     /**

  15.      * 当ListView数据发生变化时,调用此方法来更新ListView

  16.      * 

  17.      * @param mBrandList

  18.      */

  19.     public void updateListView(List<CarBrand> list) {

  20.         this.mBrandList = list;

  21.         notifyDataSetChanged();

  22.     }


  23.     public int getCount() {

  24.         return this.mBrandList.size();

  25.     }


  26.     public Object getItem(int position) {

  27.         return mBrandList.get(position);

  28.     }


  29.     public long getItemId(int position) {

  30.         return position;

  31.     }


  32.     final static class ViewHolder {

  33.         TextView tvLetter;

  34.         TextView tvTitle;

  35.         ImageView carBrand;

  36.         ImageView close;

  37.         View body;

  38.         View titleBar;

  39.     }


  40.     final static class CarStyleViewHolder {

  41.         ExpandableListView styleList;

  42.     }


  43.     /**

  44.      * 根据ListView的当前位置获取分类的首字母的Char ascii值

  45.      */

  46.     public int getSectionForPosition(int position) {

  47.         return mBrandList.get(position).mSortLetters.charAt(0);

  48.     }


  49.     /**

  50.      * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置

  51.      */

  52.     public int getPositionForSection(int section) {

  53.         for (int i = 0; i < getCount(); i++) {

  54.             String sortStr = mBrandList.get(i).mSortLetters;

  55.             char firstChar = sortStr.toUpperCase().charAt(0);

  56.             if (firstChar == section) {

  57.                 return i;

  58.             }

  59.         }


  60.         return -1;

  61.     }


  62.     /**

  63.      * 提取英文的首字母,非英文字母用#代替。

  64.      * 

  65.      * @param str

  66.      * @return

  67.      */

  68.     private String getAlpha(String str) {

  69.         String sortStr = str.trim().substring(0, 1).toUpperCase();

  70.         // 正则表达式,判断首字母是否是英文字母

  71.         if (sortStr.matches("[A-Z]")) {

  72.             return sortStr;

  73.         } else {

  74.             return "#";

  75.         }

  76.     }


  77.     @Override

  78.     public Object[] getSections() {

  79.         return null;

  80.     }


  81.     @Override

  82.     public int getGroupCount() {

  83.         return mBrandList == null ? 0 : mBrandList.size();

  84.     }


  85.     @Override

  86.     public int getChildrenCount(int groupPosition) {

  87.         return mBrandList == null ? 0

  88.                 : (mBrandList.get(groupPosition) == null ? 0 : (mBrandList

  89.                         .get(groupPosition).mCarStyleList == null ? 0

  90.                         : mBrandList.get(groupPosition).mCarStyleList.size()));

  91.     }


  92.     @Override

  93.     public Object getGroup(int groupPosition) {

  94.         return mBrandList.get(groupPosition);

  95.     }


  96.     @Override

  97.     public Object getChild(int groupPosition, int childPosition) {

  98.         return mBrandList.get(groupPosition).mCarStyleList.get(childPosition);

  99.     }


  100.     @Override

  101.     public long getGroupId(int groupPosition) {

  102.         return groupPosition;

  103.     }


  104.     @Override

  105.     public long getChildId(int groupPosition, int childPosition) {

  106.         return groupPosition;

  107.     }


  108.     @Override

  109.     public boolean hasStableIds() {

  110.         return false;

  111.     }


  112.     @Override

  113.     public View getGroupView(int groupPosition, boolean isExpanded,

  114.             View convertView, ViewGroup parent) {

  115.         ViewHolder viewHolder = null;

  116.         final CarBrand mContent = mBrandList.get(groupPosition);

  117.         if (convertView == null) {

  118.             viewHolder = new ViewHolder();

  119.             convertView = LayoutInflater.from(mContext).inflate(

  120.                     R.layout.car_brand_item, null);

  121.             viewHolder.tvTitle = (TextView) convertView

  122.                     .findViewById(R.id.title);

  123.             viewHolder.tvLetter = (TextView) convertView

  124.                     .findViewById(R.id.catalog);

  125.             viewHolder.carBrand = (ImageView) convertView

  126.                     .findViewById(R.id.item_img);

  127.             viewHolder.close = (ImageView) convertView

  128.                     .findViewById(R.id.close_img);

  129.             viewHolder.body = convertView.findViewById(R.id.item_body);

  130.             viewHolder.titleBar = convertView.findViewById(R.id.title_bar);

  131.             convertView.setTag(viewHolder);

  132.         } else {

  133.             viewHolder = (ViewHolder) convertView.getTag();

  134.         }


  135.         // 根据position获取分类的首字母的Char ascii值

  136.         int section = getSectionForPosition(groupPosition);


  137.         // 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现

  138.         if (groupPosition == getPositionForSection(section)) {

  139.             viewHolder.titleBar.setVisibility(View.VISIBLE);

  140.             viewHolder.tvLetter.setText(mContent.mSortLetters);

  141.         } else {

  142.             viewHolder.titleBar.setVisibility(View.GONE);

  143.         }


  144.         viewHolder.tvTitle

  145.                 .setText(this.mBrandList.get(groupPosition).mBrandName);


  146.         if (isExpanded) {

  147.             viewHolder.close.setVisibility(View.VISIBLE);

  148.         } else {

  149.             viewHolder.close.setVisibility(View.GONE);

  150.         }


  151.         return convertView;


  152.     }


  153.     @Override

  154.     public View getChildView(int groupPosition, int childPosition,

  155.             boolean isLastChild, View convertView, ViewGroup parent) {

  156.         CarStyleAdapter carStyleAdapter = new CarStyleAdapter(mContext,

  157.                 mBrandList.get(groupPosition).mCarStyleList.get(childPosition));

  158.         CustExpListview SecondLevelexplv = new CustExpListview(mContext);

  159.         SecondLevelexplv.setAdapter(carStyleAdapter);

  160.         SecondLevelexplv.setGroupIndicator(null);

  161.         return SecondLevelexplv;

  162.     }


  163.     @Override

  164.     public boolean isChildSelectable(int groupPosition, int childPosition) {

  165.         return true;

  166.     }


  167.     public class CustExpListview extends ExpandableListView {


  168.         public CustExpListview(Context context) {

  169.             super(context);

  170.         }


  171.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  172.             widthMeasureSpec = MeasureSpec.makeMeasureSpec(960,

  173.                     MeasureSpec.AT_MOST);

  174.             heightMeasureSpec = MeasureSpec.makeMeasureSpec(600,

  175.                     MeasureSpec.AT_MOST);

  176.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);

  177.         }

  178.     }

  179. }

      
     4 .将这个Adapter加到ExpandableListView中就可以实现二级的扩展菜单了,下面来实现带GridView的三级扩展菜单。与二级的不同,三级的GridView必须使用一个来实现,也就是说
     每个车型下面的n个年款使用一个GridView来展示。所以需要注意的就是返回ChildCount的时候只能返回1。这个Adapter的实现如下:


点击(此处)折叠或打开

  1. /**

  2.  * 显示车型和年款的adapter

  3.  * 

  4.  * @author liyanshun 2014-2-27

  5.  */

  6. public class CarStyleAdapter extends BaseExpandableListAdapter implements

  7.         OnItemClickListener {

  8.     private static final String TAG = "CarStyleAdapter";

  9.     private CarStyle mCarStyle = null;

  10.     private Context mContext;


  11.     public CarStyleAdapter(Context mContext, CarStyle carStyle) {

  12.         this.mContext = mContext;

  13.         this.mCarStyle = carStyle;

  14.     }


  15.     @Override

  16.     public int getGroupCount() {

  17.         return mCarStyle == null ? 0 : 1;

  18.     }


  19.     @Override

  20.     public int getChildrenCount(int groupPosition) {

  21.         //只显示一个Child,否则会造成GridView的重复显示

  22.         return mCarStyle == null ? 0 : (mCarStyle.mModelList == null ? 0 : 1);

  23.     }


  24.     @Override

  25.     public Object getGroup(int groupPosition) {

  26.         return mCarStyle;

  27.     }


  28.     @Override

  29.     public Object getChild(int groupPosition, int childPosition) {

  30.         return mCarStyle.mModelList.get(childPosition);

  31.     }


  32.     @Override

  33.     public long getGroupId(int groupPosition) {

  34.         return groupPosition;

  35.     }


  36.     @Override

  37.     public long getChildId(int groupPosition, int childPosition) {

  38.         return groupPosition;

  39.     }


  40.     @Override

  41.     public boolean hasStableIds() {

  42.         return false;

  43.     }


  44.     @Override

  45.     public View getGroupView(int groupPosition, boolean isExpanded,

  46.             View convertView, ViewGroup parent) {

  47.         Logger.d(TAG, "groupPosition:" + groupPosition);

  48.         ViewHolder viewHolder = null;

  49.         if (convertView == null) {

  50.             viewHolder = new ViewHolder();

  51.             convertView = LayoutInflater.from(mContext).inflate(

  52.                     R.layout.car_style_item, null);

  53.             viewHolder.tvTitle = (TextView) convertView

  54.                     .findViewById(R.id.style_name);

  55.             viewHolder.bottmLine = (View) convertView

  56.                     .findViewById(R.id.style_bottom_line);

  57.             viewHolder.styleArrow = (ImageView) convertView

  58.                     .findViewById(R.id.style_arrow);

  59.             viewHolder.closeView = (ImageView) convertView

  60.                     .findViewById(R.id.style_close_img);

  61.             convertView.setTag(viewHolder);

  62.         } else {

  63.             viewHolder = (ViewHolder) convertView.getTag();

  64.         }

  65.         viewHolder.tvTitle.setText(mCarStyle.mStyleName);

  66.         if (isExpanded) {

  67.             viewHolder.styleArrow.setVisibility(View.VISIBLE);

  68.             viewHolder.closeView.setVisibility(View.VISIBLE);

  69.         } else {

  70.             viewHolder.styleArrow.setVisibility(View.GONE);

  71.             viewHolder.closeView.setVisibility(View.GONE);

  72.         }

  73.         return convertView;

  74.     }


  75.     @Override

  76.     public View getChildView(int groupPosition, int childPosition,

  77.             boolean isLastChild, View convertView, ViewGroup parent) {

  78.         GridView gridView = null;

  79.         if (convertView == null) {

  80.             LayoutInflater layoutInflater = (LayoutInflater) mContext

  81.                     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  82.             convertView = layoutInflater.inflate(R.layout.car_model_grid, null);


  83.             gridView = (GridView) convertView.findViewById(R.id.gridview);

  84.             gridView.setNumColumns(2);// 设置每行列数

  85.             gridView.setGravity(Gravity.CENTER);// 位置居中

  86.             gridView.setHorizontalSpacing(10);// 水平间隔


  87.             gridView.setAdapter(new GridAdapter());


  88.             //计算并设置gridView的高度

  89.             final int rowHeightDp = 40;

  90.             final float ROW_HEIGHT = mContext.getResources()

  91.                     .getDisplayMetrics().density * rowHeightDp;

  92.             double size = mCarStyle.mModelList.size();

  93.             int rowCount = (int) Math.ceil(size/2);

  94.             final int GRID_HEIGHT = (int) (ROW_HEIGHT * rowCount);

  95.             gridView.getLayoutParams().height = GRID_HEIGHT;

  96.             gridView.setOnItemClickListener(this);

  97.         }


  98.         return convertView;


  99.     }


  100.     @Override

  101.     public boolean isChildSelectable(int groupPosition, int childPosition) {

  102.         return true;

  103.     }


  104.     final static class ViewHolder {

  105.         TextView tvTitle;

  106.         View bottmLine;

  107.         ImageView styleArrow;

  108.         ImageView closeView;

  109.     }


  110.     private class GridAdapter extends BaseAdapter {

  111.         @Override

  112.         public int getCount() {

  113.             return mCarStyle.mModelList.size();

  114.         }


  115.         @Override

  116.         public Object getItem(int position) {

  117.             return mCarStyle.mModelList.get(position);

  118.         }


  119.         @Override

  120.         public long getItemId(int position) {

  121.             return 0;

  122.         }


  123.         @Override

  124.         public View getView(int position, View convertView, ViewGroup parent) {

  125.             ViewHolder viewHolder = null;

  126.             if (convertView == null) {

  127.                 viewHolder = new ViewHolder();

  128.                 convertView = LayoutInflater.from(mContext).inflate(

  129.                         R.layout.car_model_item, null);

  130.                 viewHolder.tvTitle = (TextView) convertView

  131.                         .findViewById(R.id.name);

  132.                 convertView.setTag(viewHolder);

  133.             } else {

  134.                 viewHolder = (ViewHolder) convertView.getTag();

  135.             }

  136.             viewHolder.tvTitle.setText((mCarStyle.mModelList.get(position)));

  137.             return convertView;

  138.         }


  139.     }


  140.     @Override

  141.     public void onItemClick(AdapterView<?> parent, View view, int position,

  142.             long id) {

  143.         Toast.makeText(mContext, "" + position, Toast.LENGTH_SHORT).show();

  144.     }

  145. }


    5.好了,最重要的两个Adapter已经实现了,只需要将其加入了界面中就可以了。在这里我使用了一个fragment来进行展示:
   

点击(此处)折叠或打开

  1. /**

  2.  * 选择汽车品牌

  3.  * 

  4.  * @author liyanshun 2014-2-21

  5.  */

  6. public class SelectBrandFragment extends FatherFragment implements

  7.         OnClickListener, OnItemClickListener {

  8.     private static final String TAG = "SelectBrandFragment";

  9.     private ExpandableListView mSortListView;

  10.     private SideBar mSideBar;

  11.     private TextView mDialog;

  12.     private CarBrandAdapter mAdapter;

  13.     /**

  14.      * 汉字转换成拼音的类

  15.      */

  16.     private CharacterParser characterParser;

  17.     private List<CarBrand> SourceDateList;


  18.     /**

  19.      * 根据拼音来排列ListView里面的数据类

  20.      */

  21.     private PinyinComparator pinyinComparator;


  22.     String[] data={"奥迪","宝马","奔驰","雪铁龙","大众","牧马人"};

  23.     @Override

  24.     public void onStart() {

  25.         super.onStart();

  26.         Logger.d(TAG,"onStart");

  27.         //已经设置过layout了,无需再次设定

  28.         if(mLayoutAdded){

  29.             return;

  30.         }

  31.         super.setLayout(R.layout.select_brand);

  32.         mSortListView = (ExpandableListView) mBodyView.findViewById(R.id.lv);

  33.         mSideBar = (SideBar) mBodyView.findViewById(R.id.sidrbar);

  34.         mDialog = (TextView) mBodyView.findViewById(R.id.dialog);

  35.         mSideBar.setTextView(mDialog);

  36.         mTitle.setText(R.string.select_brand);

  37.         mRightTitle.setText(R.string.jump);

  38.         mRightTitle.setOnClickListener(this);


  39.         // 实例化汉字转拼音类

  40.         characterParser = CharacterParser.getInstance();

  41.         pinyinComparator = new PinyinComparator();


  42.         mSortListView.setOnItemClickListener(this);

  43.         // 设置右侧触摸监听

  44.         mSideBar.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {


  45.             @Override

  46.             public void onTouchingLetterChanged(String s) {

  47.                 // 该字母首次出现的位置

  48.                 int position = mAdapter.getPositionForSection(s.charAt(0));

  49.                 if (position != -1) {

  50.                     mSortListView.setSelection(position);

  51.                 }


  52.             }

  53.         });


  54.          SourceDateList = filledData(data);

  55. //        

  56. //        // 根据a-z进行排序源数据

  57.         Collections.sort(SourceDateList, pinyinComparator);

  58.         mAdapter = new CarBrandAdapter(mContext, SourceDateList);

  59. //        CarStyleAdapter carStyleAdapter=new CarStyleAdapter(mContext,SourceDateList.get(2).mCarStyleList);

  60.         mSortListView.setAdapter(mAdapter);

  61.     }


  62.     @Override

  63.     public void onClick(View v) {

  64.         int id = v.getId();

  65.         switch (id) {

  66.         case R.id.right_button:

  67.             Logger.d(TAG,"rightbutton");

  68.             break;

  69.         }


  70.     }


  71.     @Override

  72.     public void onItemClick(AdapterView<?> parent, View view, int position,

  73.             long id) {

  74.         Logger.d(TAG,"onItemClick:"+position);


  75.     }

  76.     

  77.     /**

  78.      * 为ListView填充数据

  79.      * @param date

  80.      * @return

  81.      */

  82.     private List<CarBrand> filledData(String [] date){

  83.         List<CarBrand> mBrandList = new ArrayList<CarBrand>();

  84.         for(int i=0; i<date.length; i++){

  85.             CarBrand carBrand = new CarBrand();

  86.             carBrand.mBrandName=date[i];

  87.             //汉字转换成拼音

  88.             String pinyin = characterParser.getSelling(date[i]);

  89.             String sortString = pinyin.substring(0, 1).toUpperCase();

  90.             

  91.             // 正则表达式,判断首字母是否是英文字母

  92.             if(sortString.matches("[A-Z]")){

  93.                 carBrand.mSortLetters=sortString.toUpperCase();

  94.             }else{

  95.                 carBrand.mSortLetters="#";

  96.             }

  97.             ArrayList<CarStyle> mStyleList = new ArrayList<CarStyle>();

  98.             for(int j=0;j<=i;j++){

  99.                 CarStyle carStyle=new CarStyle();

  100.                 carStyle.mStyleName=date[i]+"201"+j;

  101.                 ArrayList<String> mModelList = new ArrayList<String>();

  102.                 for(int k=0;k<=j;k++){

  103.                     mModelList.add(carStyle.mStyleName+":"+"手动款");

  104.                 }

  105.                 carStyle.mModelList=mModelList;

  106.                 mStyleList.add(carStyle);

  107.             }

  108.             carBrand.mCarStyleList=mStyleList;

  109.             mBrandList.add(carBrand);

  110.         }

  111.         return mBrandList;

  112.         

  113.     }

  114. }


      6.大功告成了,来看一下显示的效果吧。









android中用ExpandableListView实现三级扩展列表