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. * 为ListView填充数据
  78. * @param date
  79. * @return
  80. */
  81. private List<CarBrand> filledData(String [] date){
  82. List<CarBrand> mBrandList = new ArrayList<CarBrand>();
  83. for(int i=0; i<date.length; i++){
  84. CarBrand carBrand = new CarBrand();
  85. carBrand.mBrandName=date[i];
  86. //汉字转换成拼音
  87. String pinyin = characterParser.getSelling(date[i]);
  88. String sortString = pinyin.substring(0, 1).toUpperCase();
  89. // 正则表达式,判断首字母是否是英文字母
  90. if(sortString.matches("[A-Z]")){
  91. carBrand.mSortLetters=sortString.toUpperCase();
  92. }else{
  93. carBrand.mSortLetters="#";
  94. }
  95. ArrayList<CarStyle> mStyleList = new ArrayList<CarStyle>();
  96. for(int j=0;j<=i;j++){
  97. CarStyle carStyle=new CarStyle();
  98. carStyle.mStyleName=date[i]+"201"+j;
  99. ArrayList<String> mModelList = new ArrayList<String>();
  100. for(int k=0;k<=j;k++){
  101. mModelList.add(carStyle.mStyleName+":"+"手动款");
  102. }
  103. carStyle.mModelList=mModelList;
  104. mStyleList.add(carStyle);
  105. }
  106. carBrand.mCarStyleList=mStyleList;
  107. mBrandList.add(carBrand);
  108. }
  109. return mBrandList;
  110. }
  111. }

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

时间: 2024-10-07 22:32:19

android中用ExpandableListView实现三级扩展列表的相关文章

三级扩展列表 学习心得

@Override public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { final ExpandableListView expandableListView = getExpandableListView(); /** * 这里每个exListView 只有一个元素, 每个元素代表二级目录的每

22.Android之ExpandableListView树形列表学习

Android经常用到树形菜单,一般ExpandableListView可以满足这个需要,今天学习下. XML代码: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent

Android 利用ExpandableListView显示和查询仿QQ分组列表用户信息

在我们的项目开发过程中,经常会对用户的信息进行分组,即通过组来显示用户的信息,同时通过一定的查询条件来显示查询后的相关用户信息,并且通过颜色选择器来设置列表信息的背景颜色. 其中借鉴xiaanming:http://blog.csdn.net/xiaanming/article/details/12684155 下面来看看项目运行后的效果图以及代码结构图: 下面通过代码来实现整个效果. 1.主界面布局activity_main.xml <span style="font-size:18px

ExpandableListView的使用多级列表

多级列表ExpandableListView 扩展列表能够显示一个指示在每项显示项的当前状态(状态通常是一个扩展的组,组的孩子,或倒塌,最后一个孩子).使用setchildindicator(drawable)或setgroupindicator(drawable)(或相应的XML属性)来设置这些指标,一个默认的风格多级列表提供指标,将示给意见多级列表.布局android.r.layout.simple_expandable_list_item_1和android.r.layout.simple

android的ExpandableListView

activity_main.xml <RelativeLayout 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_

Android中ExpandableListView控件基本使用

本文採用一个Demo来展示Android中ExpandableListView控件的使用,如怎样在组/子ListView中绑定数据源.直接上代码例如以下: 程序结构图: layout文件夹下的 main.xml 文件源代码例如以下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/a

Android在ExpandableListView控制的基本使用

在本文中,Demo为了展示Android在ExpandableListView用途管制.如该组/儿子ListView绑定数据源. 直接上代码例如以下: 程序结构图: layout文件夹下的 main.xml 文件源代码例如以下: <? xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/an

Android自定义控件---继承ProgressBar功能扩展

一.前言 前一段时间在做视频开发,由于本人刚接触视频开发这块,所以 领导没有对我提很高的要求,仅仅要求能够播放本地视频即可. 我想怎么简单怎么做.于是选择用Android VideoView控件来播放视频 (后面发现VideoView的灵活性实在太差,我不想吐槽). 最终的效果图: 视频全屏效果 这次的任务主要难度在于进度条这个控件.各位可以从上面的两张图中看到,进度条被分 为三段.每段表示一个视频,并且每个视频的长度不一,也就意味着每段视频进度条的前进速度是不相同的. 难点总结: 1.自定义控

(转载)自定义ExpandableListView,实现二级列表效果

先看效果图: 上图是我们要实现的效果,那么现在我们开始着手去做,主要分为以下几步: 一丶我们需要根据效果图去思考该如何动手,从上图分析看,我们可以用一个相对布局RelativeLayout来完成group(一级item)的布局设计,至于child(二级item)的布局,我们可以用一个TextView来完成,当然,如果如要更复杂的效果可以参照一级item的布局方式进行. 以下是main.xml丶group.xml和child.xml的布局: main.xml <?xml version="1