工作中遇到一个选择车型的问题,需要在扩展列表中选择车辆品牌->车型->年款,所以必须得使用三级的扩展列表来实现,而且第三级还得使用GridView来展示。下面就一步步来吧。
1.定义需要使用的车型类,每个车辆品牌下面包含n个车型,每个车型下面包含n个年款
点击(此处)折叠或打开
- /**
- * 汽车的品牌类
- *
- * @author liyanshun 2014-2-21
- */
- public class CarBrand {
- /**
- * 汽车的品牌名字
- */
- public String mBrandName;
- public String mSortLetters;
- /** 该品牌下包含的汽车类型 */
- public ArrayList<CarStyle> mCarStyleList;
- }
- /**
- * 汽车车类型
- *
- * @author liyanshun 2014-2-26
- */
- public class CarStyle {
- /** 汽车的类型名 */
- public String mStyleName;
- /** 该类型下包含的年款 */
- public ArrayList<String> mModelList;
- }
2.列表要可以按照首字母快速定位,这个功能参考了别人的实现,详情见这篇博客:http://blog.csdn.net/xiaanming/article/details/12684155
3.实现第一级扩展列表。第一级比较好实现,只用使用一个ExpandableListView,并定义Adapter就可以了。需要注意的是每个品牌下面的子View个数即为其包含的车型个数,而每个车型都是一个新 的ExpandableListView。当然也可以使用一个ExpandableListView来展示所有的车型,但是那样的话会导致点击靠下的车型时,其后面的车型可能会被后一项的品牌给覆盖掉。所以在这里使用多了多 个ExpandableListView。Adapter的实现如下:
点击(此处)折叠或打开
- /**
- * 用来选择车辆品牌并且带字母排序的adapter
- *
- * @author liyanshun 2014-2-21
- */
- public class CarBrandAdapter extends BaseExpandableListAdapter implements
- SectionIndexer {
- private List<CarBrand> mBrandList = null;
- private Context mContext;
- public CarBrandAdapter(Context mContext, List<CarBrand> list) {
- this.mContext = mContext;
- this.mBrandList = list;
- }
- /**
- * 当ListView数据发生变化时,调用此方法来更新ListView
- *
- * @param mBrandList
- */
- public void updateListView(List<CarBrand> list) {
- this.mBrandList = list;
- notifyDataSetChanged();
- }
- public int getCount() {
- return this.mBrandList.size();
- }
- public Object getItem(int position) {
- return mBrandList.get(position);
- }
- public long getItemId(int position) {
- return position;
- }
- final static class ViewHolder {
- TextView tvLetter;
- TextView tvTitle;
- ImageView carBrand;
- ImageView close;
- View body;
- View titleBar;
- }
- final static class CarStyleViewHolder {
- ExpandableListView styleList;
- }
- /**
- * 根据ListView的当前位置获取分类的首字母的Char ascii值
- */
- public int getSectionForPosition(int position) {
- return mBrandList.get(position).mSortLetters.charAt(0);
- }
- /**
- * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置
- */
- public int getPositionForSection(int section) {
- for (int i = 0; i < getCount(); i++) {
- String sortStr = mBrandList.get(i).mSortLetters;
- char firstChar = sortStr.toUpperCase().charAt(0);
- if (firstChar == section) {
- return i;
- }
- }
- return -1;
- }
- /**
- * 提取英文的首字母,非英文字母用#代替。
- *
- * @param str
- * @return
- */
- private String getAlpha(String str) {
- String sortStr = str.trim().substring(0, 1).toUpperCase();
- // 正则表达式,判断首字母是否是英文字母
- if (sortStr.matches("[A-Z]")) {
- return sortStr;
- } else {
- return "#";
- }
- }
- @Override
- public Object[] getSections() {
- return null;
- }
- @Override
- public int getGroupCount() {
- return mBrandList == null ? 0 : mBrandList.size();
- }
- @Override
- public int getChildrenCount(int groupPosition) {
- return mBrandList == null ? 0
- : (mBrandList.get(groupPosition) == null ? 0 : (mBrandList
- .get(groupPosition).mCarStyleList == null ? 0
- : mBrandList.get(groupPosition).mCarStyleList.size()));
- }
- @Override
- public Object getGroup(int groupPosition) {
- return mBrandList.get(groupPosition);
- }
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return mBrandList.get(groupPosition).mCarStyleList.get(childPosition);
- }
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return groupPosition;
- }
- @Override
- public boolean hasStableIds() {
- return false;
- }
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- ViewHolder viewHolder = null;
- final CarBrand mContent = mBrandList.get(groupPosition);
- if (convertView == null) {
- viewHolder = new ViewHolder();
- convertView = LayoutInflater.from(mContext).inflate(
- R.layout.car_brand_item, null);
- viewHolder.tvTitle = (TextView) convertView
- .findViewById(R.id.title);
- viewHolder.tvLetter = (TextView) convertView
- .findViewById(R.id.catalog);
- viewHolder.carBrand = (ImageView) convertView
- .findViewById(R.id.item_img);
- viewHolder.close = (ImageView) convertView
- .findViewById(R.id.close_img);
- viewHolder.body = convertView.findViewById(R.id.item_body);
- viewHolder.titleBar = convertView.findViewById(R.id.title_bar);
- convertView.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- // 根据position获取分类的首字母的Char ascii值
- int section = getSectionForPosition(groupPosition);
- // 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现
- if (groupPosition == getPositionForSection(section)) {
- viewHolder.titleBar.setVisibility(View.VISIBLE);
- viewHolder.tvLetter.setText(mContent.mSortLetters);
- } else {
- viewHolder.titleBar.setVisibility(View.GONE);
- }
- viewHolder.tvTitle
- .setText(this.mBrandList.get(groupPosition).mBrandName);
- if (isExpanded) {
- viewHolder.close.setVisibility(View.VISIBLE);
- } else {
- viewHolder.close.setVisibility(View.GONE);
- }
- return convertView;
- }
- @Override
- public View getChildView(int groupPosition, int childPosition,
- boolean isLastChild, View convertView, ViewGroup parent) {
- CarStyleAdapter carStyleAdapter = new CarStyleAdapter(mContext,
- mBrandList.get(groupPosition).mCarStyleList.get(childPosition));
- CustExpListview SecondLevelexplv = new CustExpListview(mContext);
- SecondLevelexplv.setAdapter(carStyleAdapter);
- SecondLevelexplv.setGroupIndicator(null);
- return SecondLevelexplv;
- }
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
- public class CustExpListview extends ExpandableListView {
- public CustExpListview(Context context) {
- super(context);
- }
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(960,
- MeasureSpec.AT_MOST);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(600,
- MeasureSpec.AT_MOST);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
- }
4 .将这个Adapter加到ExpandableListView中就可以实现二级的扩展菜单了,下面来实现带GridView的三级扩展菜单。与二级的不同,三级的GridView必须使用一个来实现,也就是说
每个车型下面的n个年款使用一个GridView来展示。所以需要注意的就是返回ChildCount的时候只能返回1。这个Adapter的实现如下:
点击(此处)折叠或打开
- /**
- * 显示车型和年款的adapter
- *
- * @author liyanshun 2014-2-27
- */
- public class CarStyleAdapter extends BaseExpandableListAdapter implements
- OnItemClickListener {
- private static final String TAG = "CarStyleAdapter";
- private CarStyle mCarStyle = null;
- private Context mContext;
- public CarStyleAdapter(Context mContext, CarStyle carStyle) {
- this.mContext = mContext;
- this.mCarStyle = carStyle;
- }
- @Override
- public int getGroupCount() {
- return mCarStyle == null ? 0 : 1;
- }
- @Override
- public int getChildrenCount(int groupPosition) {
- //只显示一个Child,否则会造成GridView的重复显示
- return mCarStyle == null ? 0 : (mCarStyle.mModelList == null ? 0 : 1);
- }
- @Override
- public Object getGroup(int groupPosition) {
- return mCarStyle;
- }
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return mCarStyle.mModelList.get(childPosition);
- }
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return groupPosition;
- }
- @Override
- public boolean hasStableIds() {
- return false;
- }
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- Logger.d(TAG, "groupPosition:" + groupPosition);
- ViewHolder viewHolder = null;
- if (convertView == null) {
- viewHolder = new ViewHolder();
- convertView = LayoutInflater.from(mContext).inflate(
- R.layout.car_style_item, null);
- viewHolder.tvTitle = (TextView) convertView
- .findViewById(R.id.style_name);
- viewHolder.bottmLine = (View) convertView
- .findViewById(R.id.style_bottom_line);
- viewHolder.styleArrow = (ImageView) convertView
- .findViewById(R.id.style_arrow);
- viewHolder.closeView = (ImageView) convertView
- .findViewById(R.id.style_close_img);
- convertView.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- viewHolder.tvTitle.setText(mCarStyle.mStyleName);
- if (isExpanded) {
- viewHolder.styleArrow.setVisibility(View.VISIBLE);
- viewHolder.closeView.setVisibility(View.VISIBLE);
- } else {
- viewHolder.styleArrow.setVisibility(View.GONE);
- viewHolder.closeView.setVisibility(View.GONE);
- }
- return convertView;
- }
- @Override
- public View getChildView(int groupPosition, int childPosition,
- boolean isLastChild, View convertView, ViewGroup parent) {
- GridView gridView = null;
- if (convertView == null) {
- LayoutInflater layoutInflater = (LayoutInflater) mContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = layoutInflater.inflate(R.layout.car_model_grid, null);
- gridView = (GridView) convertView.findViewById(R.id.gridview);
- gridView.setNumColumns(2);// 设置每行列数
- gridView.setGravity(Gravity.CENTER);// 位置居中
- gridView.setHorizontalSpacing(10);// 水平间隔
- gridView.setAdapter(new GridAdapter());
- //计算并设置gridView的高度
- final int rowHeightDp = 40;
- final float ROW_HEIGHT = mContext.getResources()
- .getDisplayMetrics().density * rowHeightDp;
- double size = mCarStyle.mModelList.size();
- int rowCount = (int) Math.ceil(size/2);
- final int GRID_HEIGHT = (int) (ROW_HEIGHT * rowCount);
- gridView.getLayoutParams().height = GRID_HEIGHT;
- gridView.setOnItemClickListener(this);
- }
- return convertView;
- }
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
- final static class ViewHolder {
- TextView tvTitle;
- View bottmLine;
- ImageView styleArrow;
- ImageView closeView;
- }
- private class GridAdapter extends BaseAdapter {
- @Override
- public int getCount() {
- return mCarStyle.mModelList.size();
- }
- @Override
- public Object getItem(int position) {
- return mCarStyle.mModelList.get(position);
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder viewHolder = null;
- if (convertView == null) {
- viewHolder = new ViewHolder();
- convertView = LayoutInflater.from(mContext).inflate(
- R.layout.car_model_item, null);
- viewHolder.tvTitle = (TextView) convertView
- .findViewById(R.id.name);
- convertView.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- viewHolder.tvTitle.setText((mCarStyle.mModelList.get(position)));
- return convertView;
- }
- }
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
- Toast.makeText(mContext, "" + position, Toast.LENGTH_SHORT).show();
- }
- }
5.好了,最重要的两个Adapter已经实现了,只需要将其加入了界面中就可以了。在这里我使用了一个fragment来进行展示:
点击(此处)折叠或打开
- /**
- * 选择汽车品牌
- *
- * @author liyanshun 2014-2-21
- */
- public class SelectBrandFragment extends FatherFragment implements
- OnClickListener, OnItemClickListener {
- private static final String TAG = "SelectBrandFragment";
- private ExpandableListView mSortListView;
- private SideBar mSideBar;
- private TextView mDialog;
- private CarBrandAdapter mAdapter;
- /**
- * 汉字转换成拼音的类
- */
- private CharacterParser characterParser;
- private List<CarBrand> SourceDateList;
- /**
- * 根据拼音来排列ListView里面的数据类
- */
- private PinyinComparator pinyinComparator;
- String[] data={"奥迪","宝马","奔驰","雪铁龙","大众","牧马人"};
- @Override
- public void onStart() {
- super.onStart();
- Logger.d(TAG,"onStart");
- //已经设置过layout了,无需再次设定
- if(mLayoutAdded){
- return;
- }
- super.setLayout(R.layout.select_brand);
- mSortListView = (ExpandableListView) mBodyView.findViewById(R.id.lv);
- mSideBar = (SideBar) mBodyView.findViewById(R.id.sidrbar);
- mDialog = (TextView) mBodyView.findViewById(R.id.dialog);
- mSideBar.setTextView(mDialog);
- mTitle.setText(R.string.select_brand);
- mRightTitle.setText(R.string.jump);
- mRightTitle.setOnClickListener(this);
- // 实例化汉字转拼音类
- characterParser = CharacterParser.getInstance();
- pinyinComparator = new PinyinComparator();
- mSortListView.setOnItemClickListener(this);
- // 设置右侧触摸监听
- mSideBar.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {
- @Override
- public void onTouchingLetterChanged(String s) {
- // 该字母首次出现的位置
- int position = mAdapter.getPositionForSection(s.charAt(0));
- if (position != -1) {
- mSortListView.setSelection(position);
- }
- }
- });
- SourceDateList = filledData(data);
- //
- // // 根据a-z进行排序源数据
- Collections.sort(SourceDateList, pinyinComparator);
- mAdapter = new CarBrandAdapter(mContext, SourceDateList);
- // CarStyleAdapter carStyleAdapter=new CarStyleAdapter(mContext,SourceDateList.get(2).mCarStyleList);
- mSortListView.setAdapter(mAdapter);
- }
- @Override
- public void onClick(View v) {
- int id = v.getId();
- switch (id) {
- case R.id.right_button:
- Logger.d(TAG,"rightbutton");
- break;
- }
- }
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
- Logger.d(TAG,"onItemClick:"+position);
- }
- /**
- * 为ListView填充数据
- * @param date
- * @return
- */
- private List<CarBrand> filledData(String [] date){
- List<CarBrand> mBrandList = new ArrayList<CarBrand>();
- for(int i=0; i<date.length; i++){
- CarBrand carBrand = new CarBrand();
- carBrand.mBrandName=date[i];
- //汉字转换成拼音
- String pinyin = characterParser.getSelling(date[i]);
- String sortString = pinyin.substring(0, 1).toUpperCase();
- // 正则表达式,判断首字母是否是英文字母
- if(sortString.matches("[A-Z]")){
- carBrand.mSortLetters=sortString.toUpperCase();
- }else{
- carBrand.mSortLetters="#";
- }
- ArrayList<CarStyle> mStyleList = new ArrayList<CarStyle>();
- for(int j=0;j<=i;j++){
- CarStyle carStyle=new CarStyle();
- carStyle.mStyleName=date[i]+"201"+j;
- ArrayList<String> mModelList = new ArrayList<String>();
- for(int k=0;k<=j;k++){
- mModelList.add(carStyle.mStyleName+":"+"手动款");
- }
- carStyle.mModelList=mModelList;
- mStyleList.add(carStyle);
- }
- carBrand.mCarStyleList=mStyleList;
- mBrandList.add(carBrand);
- }
- return mBrandList;
- }
- }
6.大功告成了,来看一下显示的效果吧。