Android学习之ExpandableListView

什么是ExpandableListView

ExpandableListView是扩展的ListView,继承自ListView;ExpandableListView可以实现点击展开列表,再点击收缩回去的效果。

ExpandableListView的使用

首先需要在主布局文件中声明ExpandableListView;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TestActivity3"
    android:orientation="vertical">
	
	<!--
        indicatorLeft:指示符的左边界
        indicatorRight: 指示符的右边界
        groupIndicator:设置指示符
    -->

    <ExpandableListView
        android:id="@+id/id_elv1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         android:groupIndicator="@drawable/group_indicator"
        android:indicatorRight="40dp"
        android:indicatorLeft="10dp"/>

</LinearLayout>

group_indicator.xml文件,指定不同状态下的样式

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--展开状态-->
    <item android:drawable="@drawable/indicator_expand" android:state_expanded="true">true</item>

    <!--非展开状态-->
    <item android:drawable="@drawable/indicator_collapse"></item>
</selector>

新建Chapter类和ChapterItem类;Chapter代表分组,ChapterItem代表分组中的子选项;

public class Chapter {

    private int id;
    private String name;
	//存储分组中的的子选项
    private List<ChapterItem> chapterItemList = new ArrayList<>();


    public Chapter() {
    }

    public Chapter(int id, String name) {
        this.id = id;
        this.name = name;
    }

	//向分组中添加子选项
    public void addChapterItem(ChapterItem chapterItem) {
        chapterItem.setPid(getId());
        chapterItemList.add(chapterItem);
    }

	//向分组中添加子选项
    public void addChapterItem(int cid, String cname) {
        ChapterItem chapterItem = new ChapterItem(cid, cname);
        chapterItem.setPid(getId());
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<ChapterItem> getChapterItemList() {
        return chapterItemList;
    }

    public void setChapterItemList(List<ChapterItem> chapterItemList) {
        this.chapterItemList = chapterItemList;
    }

}

ChapterItem类


public class ChapterItem {

    private String name;
    //子选项的id
    private int id;
    //子选项所在分组的id
    private int pid;

    public ChapterItem() {
    }

    public ChapterItem(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
}

ChapterLab类,使用此类模拟添加数据


public class ChapterLab {

    public static List<Chapter> getGenerateMockDats() {
        List<Chapter> datas = new ArrayList<>();

        Chapter root1 = new Chapter(1, "Android");
        Chapter root2 = new Chapter(1, "Java");
        Chapter root3 = new Chapter(1, "C/C++");
        Chapter root4 = new Chapter(1, "Python");

        root1.addChapterItem(new ChapterItem(1,"EventBus"));
        root1.addChapterItem(new ChapterItem(2, "GreenDao"));
        root1.addChapterItem(new ChapterItem(3, "Gridle"));
        root1.addChapterItem(new ChapterItem(4, "Spinner"));

        root2.addChapterItem(new ChapterItem(5,"spring"));
        root2.addChapterItem(new ChapterItem(6, "springmvc"));
        root2.addChapterItem(new ChapterItem(7, "mybatis"));
        root3.addChapterItem(new ChapterItem(8, "hibernate"));

        root3.addChapterItem(new ChapterItem(9,"STL"));
        root3.addChapterItem(new ChapterItem(10, "IO"));
        root3.addChapterItem(new ChapterItem(11, "指针"));
        root3.addChapterItem(new ChapterItem(12, "结构体"));

        root4.addChapterItem(new ChapterItem(13,"爬虫"));
        root4.addChapterItem(new ChapterItem(14, "数据分析"));
        root4.addChapterItem(new ChapterItem(15, "AI"));
        root4.addChapterItem(new ChapterItem(16, "人工智能"));

        datas.add(root1);
        datas.add(root2);
        datas.add(root3);
        datas.add(root4);

        return datas;
    }
}

item_parent_chapter.xml文件,父选项布局文件

<?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="56dp"
    android:background="#86b2f9">

    <TextView
        android:id="@+id/id_tv_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:text="Android"
        android:textSize="24dp"
        android:textStyle="bold"/>

</LinearLayout>

item_child_layoutu.xml文件,子选项布局文件

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:layout_gravity="center_vertical"
    android:textSize="16dp"
    android:id="@+id/tv_item">

</TextView>

ChapterAdapter数据适配类,这个类继承了BaseExpandableListAdapter并重写了方法,

public class ChapterAdapter extends BaseExpandableListAdapter {
	//分组的数据
    private List<Chapter> datas;

    private LayoutInflater inflater;

    private Context mContext;

    public ChapterAdapter(Context context, List<Chapter> datas) {
        this.datas = datas;
        this.mContext = context;
        inflater = LayoutInflater.from(context);
    }

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

    /**
     * 获取指定分组中子选项的个数
     * @param groupPosition
     * @return
     */
    @Override
    public int getChildrenCount(int groupPosition) {
        return datas.get(groupPosition).getChapterItemList().size();
    }

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

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

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

    /**
     * 获取子选项的ID,这个ID必须是唯一的,不能使用子条目的position
     * @param groupPosition
     * @param childPosition
     * @return
     */
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

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

    /**
     * 获取显示指定分组的视图
     * @param groupPosition
     * @param isExpanded
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        ParentViewHolder parentViewHolder = null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_parent_chapter, parent, false);
            parentViewHolder = new ParentViewHolder();
            parentViewHolder.tvName = convertView.findViewById(R.id.id_tv_parent);         
            convertView.setTag(parentViewHolder);
        } else {
            parentViewHolder = (ParentViewHolder) convertView.getTag();
        }
        Chapter chapter = datas.get(groupPosition);
        parentViewHolder.tvName.setText(chapter.getName());
  
        return convertView;
    }

    /**
     * 获取指定分组中的指定子选项的视图
     * @param groupPosition
     * @param childPosition
     * @param isLastChild
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ChildViewHolder childViewHolder = null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_child_layout, parent, false);
            childViewHolder = new ChildViewHolder();
            childViewHolder.tvName = convertView.findViewById(R.id.tv_item);
            convertView.setTag(childViewHolder);
        } else {
            childViewHolder = (ChildViewHolder) convertView.getTag();
        }
        ChapterItem chapterItem = datas.get(groupPosition).getChapterItemList().get(childPosition);
        childViewHolder.tvName.setText(chapterItem.getName());

        return convertView;
    }

    /**
     * 指定位置上的子元素是否可选中,返回true才会相应点击事件
     * @param groupPosition
     * @param childPosition
     * @return
     */
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }


    public static class ParentViewHolder {
        TextView tvName;  
    }

    public static class ChildViewHolder {
        TextView tvName;
    }
}

主Activity类

public class TestActivity3 extends AppCompatActivity {

    private ExpandableListView expandableListView;

    private ChapterAdapter chapterAdapter;

    private List<Chapter> dataList;

    private static final String TAG = "TestActivity3";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test3);

        initViews();

        initEvents();

    }

    public void initViews() {
        expandableListView = findViewById(R.id.id_elv1);
        dataList = ChapterLab.getGenerateMockDats();
        chapterAdapter = new ChapterAdapter(this,dataList);
        expandableListView.setAdapter(chapterAdapter);
    }



    public void initEvents() {
    	//设置子选项被点击监听事件
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Log.d(TAG, "onChildClick: " + groupPosition + " " + childPosition + " " + id);
                return false;
            }
        });
		//设置分组选项被点击监听事件
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                Log.d(TAG, "onGroupClick: " + groupPosition + " " + id);
                return false;
            }
        });
		//设置折叠时的监听事件
        expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
            @Override
            public void onGroupCollapse(int groupPosition) {
                Log.d(TAG, "onGroupCollapse: " + groupPosition);
            }
        });
		//设置展开时的监听事件
        expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int groupPosition) {
                Log.d(TAG, "onGroupExpand: " + groupPosition);
            }
        });
    }




}

运行后的效果:
在这里插入图片描述

ExpandableListView自带的指示器的效果不好,所以更多情况下我们会选择自定义一个指示器。
修改item_parent_chapter.xml文件,添加一个ImageView控件作为指示符

<?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="56dp"
    android:background="#86b2f9">

  <ImageView
        android:id="@+id/id_iv_indicator"
        android:layout_width="24dp"
        android:layout_gravity="center_vertical"
        android:layout_marginRight="4dp"
        android:background="@drawable/group_indicator"
        android:layout_height="24dp"/>

    <TextView
        android:id="@+id/id_tv_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:text="Android"
        android:textSize="24dp"
        android:textStyle="bold"/>

</LinearLayout>

修改主界面布局文件中ExpandableListView的android:groupIndicator属性为@null

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

    <!--
        indicatorLeft: 指示符的左边约束距离
        indicatorRight:指示符的右边约束距离
        groupIndicator:设置指示
    -->
    <ExpandableListView
        android:id="@+id/id_elv1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:groupIndicator="@null"
        android:indicatorRight="40dp"
        android:indicatorLeft="10dp"/>

</LinearLayout>

修改group_indicator.xml文件为

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!--当使用ImageView来代替indicator时,使用这个-->
    <item android:drawable="@drawable/indicator_expand" android:state_selected="true">true</item>
    <!--非展开状态-->
    <item android:drawable="@drawable/indicator_collapse"></item>
</selector>

修改ChapterAdapter类中的中的getGroupView方法和ParentViewHolder类为以下所示的样子,

@Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        ParentViewHolder parentViewHolder = null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_parent_chapter, parent, false);
            parentViewHolder = new ParentViewHolder();
            parentViewHolder.tvName = convertView.findViewById(R.id.id_tv_parent);
            parentViewHolder.imageView = convertView.findViewById(R.id.id_iv_indicator);
            convertView.setTag(parentViewHolder);
        } else {
            parentViewHolder = (ParentViewHolder) convertView.getTag();
        }
        Chapter chapter = datas.get(groupPosition);
        parentViewHolder.tvName.setText(chapter.getName());
        parentViewHolder.imageView.setSelected(isExpanded);

        return convertView;
    }java
    public static class ParentViewHolder {
        TextView tvName;
        ImageView imageView;
    }

运行后效果如图:
在这里插入图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值