ExpandableListView 是什么?

Google官方给出的解释是:
A view that shows items in a vertically scrolling two-level list. This differs from the ListView by allowing two levels: groups which can individually be expanded to show its children. The items come from the ExpandableListAdapter associated with this view.

简单翻译一下就是:
一种用于垂直滚动展示两级列表的视图,和 ListView 的不同之处就是它可以展示两级列表,分组可以单独展开显示子选项。这些选项的数据是通过 ExpandableListAdapter 关联的。这个 ExpandableListAdapter 又是什么呢?和 ListView 使用的 BaseAdapter 差不多,都是用来给 View 提供数据、 实例化子布局的。实际使用的时候实现这个接口就可以了。

我们先看一下实现的效果:

接下来是具体实现方法:

1、建立主布局

和普通的leListView一样,设置好宽度和高度,然后再绑定一个Id即可。


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

2、建立分组的布局


<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="40dp"
        android:background="@color/colorAccent">
        <ImageView
            android:layout_width="55dp"
            android:layout_height="60dp"
            android:src="@drawable/aa"
            android:id="@+id/image"/>
        <TextView
            android:id="@+id/label_expand_group"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:gravity="center_vertical"
            android:text="文本"
            android:textColor="@android:color/white"
            android:textSize="25sp" />
    </LinearLayout>

建立两个控件,ImageView用来放图标,TextView用来放分组标题。

3、建分组子选项的布局


<TextView
        android:id="@+id/label_expand_group"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_gravity="center_vertical"
        android:gravity="center"
        android:textColor="@color/black"
        android:text="文本"
        android:textSize="20sp" />

我这里的子选项只用了一个TextView来显示文本。

4、建立分组一行数据的实体类


public class Line {
    private String text;
    private int mageId;

    public Line(String text, int mageId) {
        this.text = text;
        this.mageId = mageId;
    }

    public String getText() {
        return text;
    }

    public int getMageId() {
        return mageId;
    }

    public void setText(String text) {
        this.text = text;
    }

    public void setMageId(int mageId) {
        this.mageId = mageId;
    }
}

我这里分组一行只用了两个控件,所以只需要两个变量,然后实现它的get和set方法,为了方便还实现了一个构造方法。

5、建立分组子选项的一行数据的实体类

子选项我没有使用复杂的布局,只有一个TextView来展示数据,所以我使用的是java的String类。

6、建立自定义的数据适配器:

新建一个类,使它继承自 BaseExpandableListAdapter,然后实现它的抽象方法。


public class MyBaseExpandableListAdapter extends BaseExpandableListAdapter {
    private List groupArray;//分组
    private List<List> childArray;//分组中的子选项
    private Context context;
  

    public MyBaseExpandableListAdapter(List groupArray, List<List> childArray, Context context) {
        this.groupArray = groupArray;
        this.childArray = childArray;
        this.context = context;
    }

    @Override
    //        获取分组的个数
    public int getGroupCount() {
        return groupArray.size();
    }

    @Override
    //        获取指定分组中的子选项的个数
    public int getChildrenCount(int groupPosition) {
        Log.d(TAG, "getChildrenCount: " + childArray.get(groupPosition).size());
        return childArray.get(groupPosition).size();
    }

    @Override
    //        获取指定的分组数据
    public Object getGroup(int groupPosition) {
        return childArray.get(groupPosition);
    }

    @Override
    //        获取指定分组中的指定子选项数据
    public Object getChild(int groupPosition, int childPosition) {
        return childArray.get(groupPosition).get(childPosition);
    }

    @Override
    //        获取指定分组的ID, 这个ID必须是唯一的
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    //        获取子选项的ID, 这个ID必须是唯一的
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    //        分组和子选项是否持有稳定的ID, 就是说底层数据的改变会不会影响到它们。
    public boolean hasStableIds() {
        return true;
    }

    @Override
    //        获取显示指定分组的视图
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder=new ViewHolder();
            convertView = layoutInflater.inflate(R.layout.item_expand_child, null);
            viewHolder.textView = convertView.findViewById(R.id.label_expand_group);
            viewHolder.imageView = convertView.findViewById(R.id.image);
            convertView.setTag(viewHolder);
        }
        else {
            viewHolder= (ViewHolder) convertView.getTag();
        }
        viewHolder.textView.setText(groupArray.get(groupPosition).getText());
        viewHolder.imageView.setImageResource(groupArray.get(groupPosition).getMageId());
        return convertView;
    }

    @Override
    //        获取显示指定分组中的指定子选项的视图
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            LayoutInflater layoutInflater = LayoutInflater.from(context);
            convertView = layoutInflater.inflate(R.layout.group_expand_child, null);
            viewHolder.textView = convertView.findViewById(R.id.label_expand_group);
            convertView.setTag(viewHolder);
        } else {
            viewHolder= (ViewHolder) convertView.getTag();
        }
        viewHolder.textView.setText(childArray.get(groupPosition).get(childPosition));
        return convertView;
    }

    @Override
    //        指定位置上的子元素是否可选中
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }


    private class ViewHolder {
        public TextView textView;
        public ImageView imageView;
    }
}

7、准备数据


<string-array name="citys">
        <item>湖南</item>
        <item>内蒙古</item>
        <item>浙江</item>
    </string-array>
    <string-array name="湖南">
        <item>长沙</item>
        <item>株洲</item>
        <item>湘潭</item>
        <item>衡阳</item>
        <item>邵阳</item>
        <item>岳阳</item>
        <item>常德</item>
        <item>张家界</item>
        <item>益阳</item>
        <item>郴州</item>
        <item>永州</item>
        <item>怀化</item>
        <item>娄底</item>
        <item>湘西</item>
    </string-array>
    <string-array name="内蒙古">
        <item>呼和浩特</item>
        <item>包头</item>
        <item>乌海</item>
        <item>赤峰</item>
        <item>呼伦贝尔盟</item>
        <item>阿拉善盟</item>
        <item>哲里木盟</item>
        <item>兴安盟</item>
        <item>乌兰察布盟</item>
        <item>锡林郭勒盟</item>
        <item>巴彦淖尔盟</item>
        <item>鄂市</item>
    </string-array>
    <string-array name="浙江">
        <item>杭州</item>
        <item>宁波</item>
        <item>温州</item>
        <item>嘉兴</item>
        <item>湖州</item>
        <item>绍兴</item>
        <item>金华</item>
        <item>衢州</item>
        <item>舟山</item>
        <item>台州</item>
        <item>丽水</item>
    </string-array>

这里我定义了四个数组,分别是分组名和分组子选项要显示的内容。
并且准备好图片资源

8、组合数据并调用数据适配器


public class ExpListviewActivity extends AppCompatActivity {
    private static final String TAG = "ExpListviewActivity";
    private ExpandableListView expandableListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exp_listview);
        //从资源文件中分别获取数据到数组
        String[] groupStrings = getResources().getStringArray(R.array.citys);
        int[] images = {R.drawable.aa, R.drawable.bb, R.drawable.cc, R.drawable.dd};
        String[] 湖南 = getResources().getStringArray(R.array.湖南);
        String[] 内蒙古 = getResources().getStringArray(R.array.内蒙古);
        String[] 浙江 = getResources().getStringArray(R.array.浙江);
        //将分组子选项通过二维数组组合,便于引用。
        String[][] childStrings = {湖南, 内蒙古, 浙江};

        //组合分组数据
        List groupArray = new ArrayList<>();
        for (int i = 0; i < groupStrings.length; i++) {
            groupArray.add(new Line(groupStrings[i], images[i]));
        }
        //用双层循环组合分组子选项的数据
        List<List> listChildStrings = new ArrayList<>();
        for (int i = 0; i < childStrings.length; i++) {
            List 一行 = new ArrayList<>();
            for (int j = 0; j < childStrings[i].length; j++) {
                一行.add(childStrings[i][j]);
            }
            listChildStrings.add(一行);
        }
        //实例化ExpandableListView
        expandableListView = findViewById(R.id.ExpListView);

        //创建数据适配器并调用。
        MyBaseExpandableListAdapter myBaseExpandableListAdapter =
                new MyBaseExpandableListAdapter(groupArray, listChildStrings, ExpListviewActivity.this);
        expandableListView.setAdapter(myBaseExpandableListAdapter);

        //分组的子选项点击事件
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Toast.makeText(ExpListviewActivity.this, groupPosition+"组"+childPosition+"个,从0开始数", Toast.LENGTH_SHORT).show();
                return false;
            }
        });
        //分组的点击事件
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                Toast.makeText(ExpListviewActivity.this,groupPosition+"组" , Toast.LENGTH_SHORT).show();
                return false;
            }
        });

    }
}

 

 


学习永不止步