首页 > 代码库 > android中用ExpandableListView实现三级扩展列表
android中用ExpandableListView实现三级扩展列表
工作中遇到一个选择车型的问题,需要在扩展列表中选择车辆品牌->车型->年款,所以必须得使用三级的扩展列表来实现,而且第三级还得使用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.大功告成了,来看一下显示的效果吧。
android中用ExpandableListView实现三级扩展列表