ExpandableListView简介


学习笔记,欢迎指导。

最近做了一个项目,需要一个层级列表,完成之后,就顺便来做个博客简介一下。


△基本简介

→“ExpandableListView”是对“ListView”做的扩展,“ListView”是个列表,而“ExpandableListView”则是分层的、可以扩展的、可折叠的,列表。在你学习“ExpandableListView”之前,最好已经能够熟练使用“ListView”及其优化,即“ListView”+“ArrayList”+“BaseAdapter”+“ViewHolder”(ListView的“四件套”),如果你能熟练使用“ListView”,那么“ExpandableListView”对你来说,只是加了一些功能,以及添加了一些API,很好理解。

→默认的“ExpandableListView”是两层的,效果基本就和QQ联系人的那个样子,两级。

→适配器:“ListView”可以匹配“BaseAdapter”适配器,而“ExpandableListView”对应匹配“BaseExpandableListAdapter"


△相关API简介

相比较于“ListView”,“ExpandableListView”引入了更多API(毕竟人家多一层啊),同时,其所用的适配器要重写的方法也更多,下面我们挑几个重要的来看,先了解一下这些API,看程序时就不会难理解。首先看看适配器里需要重写的API。

→public int getGroupCount();//表示分组的总个数,即一共有多少个组

→public int getChildrenCount(int groupPosition);//获取某个分组下子栏目个数

→public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent);//获取第“groupPosition"组的View对象,其中“isExpanded”表示当前位置是否展开。

→public View getChildView(int groupPosition, int childPosition,  boolean isLastChild, View convertView, ViewGroup parent);//获取第”groupPosition"组里面的"childPosition"位置上的View对象。

下面看看“ExpandableListView”本身的某一些方法:

→expandableListView.setGroupIndicator(Drawable drawable);//系统默认的“ExpandableListView"每个组View都带着一个箭头,你可以在这个方法里面设置箭头样式,如果传入”null"表示去掉默认箭头,系统自带那个箭头总是会在View的最左边,如果你想箭头放在其他地方,你就调用这个方法并传入空。

→expandableListView.setOnGroupClickListener(OnGroupClickListener onGroupClickListener);//设置每个“组”View的监听器。这里需要注意的是,“ExpandableListView"默认是会处理组View的点击事件,效果就是没按一下就会扩展对应的组、或者收起。在"OnGroupClickListener"方法里面的返回值,如果返回false,在执行完你自己的代码之后,系统还会执行默认点击事件(即展开或者收起组View),如果你在这个方法返回true,那么系统不再进行默认处理。

→expandableListView.setOnChildClickListener();//设置每个子View监听器


△示例效果

说了那么多的简介,进入正题,来看一下今天要实现的效果:


我们来做一个七大洲的国家显示,简化操作,只显示了部分国家,另外国家所在大洲来自网络,如有不对还请见谅,稍微说明一下这个效果:首先去掉了系统左边默认的箭头,其次在右边自己添加了箭头,当列表没有被展开,箭头向下,当列表被展开之后,对应的箭头就指向上面。另外需要说明的是,整个界面是在Fragment里面做的


△Fragment布局文件代码

布局文件的名字“fragment_expandable",其实只有一个控件而已

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ExpandableListView
        android:id="@+id/expandable_list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ExpandableListView>
</LinearLayout>


△Fragment主要逻辑代码

Fragment主要逻辑代码包括:对数据进行初始化、对“ExpandableListView"做初始化。

先来看看如何初始化数据的:

//    准备"ExpandableListView"用的数据
    private void dataInit(){
//        先初始化组的数据
        groupData = new String[]{"亚洲","非洲","北美洲","南美洲","南极洲","欧洲","大洋洲"};
//        初始化各个子栏目里的数据,下面各个大洲对应国家来自网络,如有错误还请见谅
        childData = new String[7][];
        childData[0] = new String[]{"中国","蒙古","朝鲜","韩国"};//亚洲
        childData[1] = new String[]{"埃及","苏丹","利比亚","突尼斯"};//非洲
        childData[2] = new String[]{"美国","加拿大"};//北美洲
        childData[3] = new String[]{"哥伦比亚","委内瑞拉"};//南美洲
        childData[4] = new String[]{"没有国家"};//南极洲
        childData[5] = new String[]{"德国","英国","爱尔兰","意大利"};//欧洲
        childData[6] = new String[]{"澳大利亚","瑙鲁共和国","瓦努阿图共和国","马绍尔群岛共和国"};//大洋洲
    }
其中"groupData"以及"childData"都是成员变量来的,真实情况下你的数据可能是来自网络,或数据库,但是这里为了简便操作,我就直接用数组了。

现在看看“ExpandalbeListView"的初始化:

//    "expandableListView"做初始化
    private void viewInit(){
        expandableListView = (ExpandableListView)layoutView.findViewById(R.id.expandable_list_view);//findViewById()找到控件
        expandableListView.setGroupIndicator(null);//去掉控件默认箭头
        expandableAdapter = new ExpandableAdapter(getActivity(),groupData,childData);//数据传递进适配器
        expandableListView.setAdapter(expandableAdapter);//适配
    }
至此为止和常规的"ListView"的主要的区别在于,1,他需要准备两套的数据,一个是组,一个是子。2,调用了个方法去掉箭头。

接着看看Fragment生命周期里的调用,也很简单:

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        layoutView = inflater.inflate(R.layout.fragment_expandable,container,false);
        dataInit();
        viewInit();
        return layoutView;
    }


△自定义布局的代码

不论是“groupView”或者"childView"都自定义一个Item,现在看看“groupView”item布局文件,文件名字:item_expandable_group

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content" android:padding="8dp"
    android:gravity="center_vertical">

    <!-- 这个控件显示大洲名字-->
    <TextView
        android:id="@+id/tv_item_expandable_group"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="24sp"/>
    <!-- 这个控件显示箭头-->
    <ImageView
        android:id="@+id/iv_item_expandable_group_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_down_arrow"/>
</LinearLayout>

这里共有两个控件,TextView用来显示大洲名字,ImageView用来显示自己那个箭头的。图片资源请自己找去啦。

然后看看“childView"item布局文件,文件名字:item_expandable_child

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent" android:padding="8dp">

    <!-- 只要一个控件显示国家名字即可-->
    <TextView
        android:id="@+id/tv_item_expandable_child"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

只有一个控件用来显示国家名字


△适配器的逻辑代码

到此为止准备工作是做完了,现在来看我们的重头戏,"BaseExpandableListAdapter"代码,它里面的关键方法我已经在前面说了,另外下面代码也有相关注释,代码如下:

public class ExpandableAdapter extends BaseExpandableListAdapter {

    private Context context;//上下文
    private String[] groupData;//组数据
    private String[][] childData;//子数据

    /**
     * 构造方法
     * @param context 上下文
     * @param groupData "组"的数据
     * @param childData "子"的数据
     */
    public ExpandableAdapter(Context context,String[] groupData,String[][] childData){
        this.context = context;
        this.groupData = groupData;
        this.childData = childData;
    }

//    获取“组”个数
    @Override
    public int getGroupCount() {
        return groupData.length;//返回组Item的个数
    }

//    获取指定组里面的子Item的个数
    @Override
    public int getChildrenCount(int groupPosition) {
        return childData[groupPosition].length;//返回某个组对应子Item个数
    }

//    返回组Item的对象
    @Override
    public Object getGroup(int groupPosition) {
        return groupData[groupPosition];//直接返回数据对象就行
    }

//    返回子Item的对象
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return childData[groupPosition][childPosition];//这里也是直接返回数据对象就行
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;//直接返回groupPosition
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return groupPosition*childPosition;//"groupPosition"和"chilPosition"结合一下再返回去
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    /*
    以下是两个ViewHolder,
    分别用于组里面的和子栏目里的
     */
    private class GroupViewHolder{
        private TextView mainLandTv;//这个表示大洲名字
        private ImageView arrowIv;
    }
    private class ChildViewHolder{
        private TextView countryNames;//这个表示国家名字
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
//        以下代码和“ListView”优化的代码差不多,即ViewHolder的使用,这里就不再多解释
        GroupViewHolder groupViewHolder = null;
        if(convertView==null){
            groupViewHolder = new GroupViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.item_expandable_group,null);
            groupViewHolder.mainLandTv = (TextView)convertView.findViewById(R.id.tv_item_expandable_group);
            groupViewHolder.arrowIv = (ImageView)convertView.findViewById(R.id.iv_item_expandable_group_arrow);
            convertView.setTag(groupViewHolder);
        }
        groupViewHolder = (GroupViewHolder)convertView.getTag();
        groupViewHolder.mainLandTv.setText(groupData[groupPosition]);//设置相对应的数据
//        根据当前栏目展开与否分别设置不同方向箭头
        if(isExpanded){
            groupViewHolder.arrowIv.setImageResource(R.drawable.ic_up_arrow);
        }else{
            groupViewHolder.arrowIv.setImageResource(R.drawable.ic_down_arrow);
        }
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
//        这里也和ViewHolder使用方式一致,不多解释
        ChildViewHolder childViewHolder = null;
        if(convertView==null){
            childViewHolder = new ChildViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.item_expandable_child,null);
            childViewHolder.countryNames = (TextView)convertView.findViewById(R.id.tv_item_expandable_child);
            convertView.setTag(childViewHolder);
        }
        childViewHolder = (ChildViewHolder)convertView.getTag();
        childViewHolder.countryNames.setText("  "+childData[groupPosition][childPosition]);//将国家的名字设置上去
        childViewHolder.countryNames.setTextSize(20);
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }
    
}

  


  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值