您的位置:首页 > 移动开发 > Android开发

android中用ExpandableListView实现三级扩展列表(附源码)

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



源码又重新整理一下放在github上。https://github.com/Chaoba/android-ThreeLevelExpandableListView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: