29. 【Android教程】折叠列表 ExpandableListView

news2025/2/23 4:12:25

本节学习一个可折叠的 ListView,可以用在一些需要分类的场景下。通过 ExpandableListView 我们可以首先在 ListView 上展示大的分类,当点击某个类别的时候再将 ListView 做一个展开,展示该类下的所有子类供用户选择。它与 ListView 的不同主要是 ExpandableListView 提供了两级列表,可以方便的做伸展和收缩。

1. ExpandableListView 的特性

ExpandableListView 继承自 ListView,这意味着它拥有 ListView 的所有属性,是 ListView 的升级版。它在 ListView 的基础上增加了子列表,当我们点击某个列表项的时候,它会展开显示所有的子 item;当我们再次点击该列表项的时候,它会收缩隐藏所有的子 item,其中子 item 相当于是一个 ListView,我们可以给它设置不同的列表样式及点击事件,通常适用于有两级分类并且子类比较多的列表场景。

2. ExpandableListView 的基本使用方法

2.1 常用属性

  • android:childDivider:
    设置子列表项的分割线样式,可以通过 drawable 或者 color 资源的方式进行配置
  • android:childIndicator:
    设置显示在子列表项旁边的 View,一般用作该列表项的指示标注
  • android:childIndicatorEnd:
    设置子列表指示View的终止位置边界
  • android:childIndicatorLeft:
    设置子列表指示View的左边界
  • android:childIndicatorRight:
    设置子列表指示View的右边界
  • android:childIndicatorStart:
    设置子列表指示View的起始位置边界
  • android:groupIndicator:
    当前分类组旁边的指示 View
  • android:indicatorEnd:
    指示 View 的终止位置边界
  • android:indicatorLeft:
    指示 View 的左边界
  • android:indicatorRight:
    指示 View 的右边界
  • android:indicatorStart:
    指示 View 的起始位置边界

2.2 常用 API

  • setChildIndicator(Drawable):
    设置展示在子列表项旁边的指示 View 的样式资源
  • setGroupIndicator(Drawable) :
    设置展示在主列表项旁边的指示 View 的样式资源,这个不会因为主列表项的伸展或者收缩而改变
  • getGroupView():
    返回这一组列表的头 View
  • getChildView():
    返回列表的子列表项

2.3 事件监听器

  • ExpandableListView.OnChildClickListener:
    该接口当中只有一个回调方法:

    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
    

    当我们点击一个子列表项的时候会回调此方法,参数解析

    • ExpandableListView parent:被点击的 ExpandableListView 对象
    • View v:被点击的具体 item 对象
    • int groupPosition:被点击的 item 所在组在主列表的位置
    • int childPosition:被点击的 item 在当前组内的位置
  • ExpandableListView.OnGroupClickListener:
    接口中只有一个回调方法:

    public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
    

该方法监听某个组的点击事件,当该组内有任意 item 被点击是回调,参数详情参见onGroupClick方法的解析

  • ExpandableListView.OnGroupCollapseListener:
    只需要实现一个方法:
public void onGroupCollapse(int groupPosition)

当某个组被折叠收缩的时候会回调此方法,参数表示被收缩的组在整个主列表中的位置

  • ExpandableListView.OnGroupExpandListener:
    该接口同样是需要实现一个方法:
    public void onGroupExpand(int groupPosition)
    

当某个组被展开的时候回调此方法

3. ExpandableListView 示例

ExpandableListView 主要是在 ListView 的基础之上加上了折叠的分类效果,所以本节就通过 ExpandableListView 实现对数据的二级分类列表效果,大类就用大家比较熟悉的某竞技游戏里面的英雄分类,而子类就是该类别里面的几个英雄。
PS:英雄分类仁者见仁智者见智,青铜选手求各位骨灰玩家轻拍

3.1 编写 Activity 的布局文件

和前几节的例子一样,我们仅需要在根布局中防止一个 ExpandableListView 即可,然后设置上相应的属性,如下:

<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/expandableListView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@android:color/darker_gray"
    android:dividerHeight="0.5dp"
    android:indicatorLeft="?android:attr/expandableListPreferredItemIndicatorLeft"
    android:padding="30dp" />

3.2 编写列表布局

列表布局类似 ListView 里面的 item 布局,但是由于 ExpandableListView 有主类和子类区分,所以这里需要提供两套布局以适应主列表和展开后的子列表:

  • 主列表布局 list_group.xml :
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/listTitle"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
      android:paddingTop="10dp"
      android:paddingBottom="10dp"
      android:textColor="@android:color/black" />
    

为了突出大分类,字体设置为黑体。

  • 子列表布局 list_item.xml :
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/expandedListItem"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
      android:paddingTop="10dp"
      android:paddingBottom="10dp" />
    

3.3 编写数据集合

本节数据会相对较多,并且有两级分类,为了代码结构清晰这里将数据单独抽离出来,与 Activity 的业务代码隔离开,新建一个数据集类 DataCollection.java:

package com.emercy.myapplication;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DataCollection {

    // 通过map存放每一个大类,key是大类类别名,value是子类List
    private static HashMap<String, List<String>> mExpandableListData = new HashMap<>();

    private static final String MASTER = "法师";
    private static final String ASSASSINATOR = "刺客";
    private static final String SHOOTER = "射手";
    private static final String TANK = "对抗";
    private static final String ASSIST = "辅助";

    // 类加载的时候初始化数据
    static {
        // 创建子类列表,存放在List当中
        List<String> master = new ArrayList<>();
        master.add("安琪拉");
        master.add("西施");
        master.add("沈梦溪");
        master.add("嫦娥");
        master.add("上官婉儿");
        master.add("不知火舞");

        List<String> assassinator = new ArrayList<>();
        assassinator.add("马超");
        assassinator.add("镜");
        assassinator.add("兰陵王");
        assassinator.add("孙悟空");
        assassinator.add("娜可露露");
        assassinator.add("元歌");

        List<String> shooter = new ArrayList<>();
        shooter.add("狄仁杰");
        shooter.add("伽罗");
        shooter.add("蒙犽");
        shooter.add("鲁班七号");
        shooter.add("孙尚香");
        shooter.add("后羿");

        List<String> tank = new ArrayList<>();
        // 咦?为什么马超出现了两次?
        // 因为作者就叫马超
        tank.add("马超");
        tank.add("盖伦");
        tank.add("芈月");
        tank.add("铠");
        tank.add("典韦");

        List<String> assist = new ArrayList<>();
        assist.add("蔡文姬");
        assist.add("小明");
        assist.add("庄周");
        assist.add("鲁班");
        assist.add("东皇太一");

        // 将所有的子类List作为Value存放到大类中
        mExpandableListData.put(MASTER, master);
        mExpandableListData.put(ASSASSINATOR, assassinator);
        mExpandableListData.put(SHOOTER, shooter);
        mExpandableListData.put(TANK, tank);
        mExpandableListData.put(ASSIST, assist);
    }

    static HashMap<String, List<String>> getData() {
        return mExpandableListData;
    }
}

该类是一个静态工具类,里面只有一个静态成员变量,用一个 map 来保存所有的列表项。map 的 key 是大类的类别名称,value 是子类的 List;子类通过一个 List 来存储所有的子类 item,最后通过getData()接口对外暴露数据集合。

3.4 编写 Adapter

ExpandableListView 的 Adapter 有些不一样,因为它需要区分主类别和子类别,会多一个 group 的概念,这里采用的是 BaseExpandableListAdapter。相比前几节使用的 baseAdapter 大体上的回调方法都类似,只是多了一些对 group 的处理。

比如 baseAdapter 的getView在 BaseExpandableListAdapter 里面分成了getGroupViewgetChildView分别用来设置主类别的 item 和子类别的 item。结合 BaseAdapter 的回调方法不难理解 BaseExpandableListAdapter,代码如下:

package com.emercy.myapplication;

import android.content.Context;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

import java.util.HashMap;
import java.util.List;

public class MyExpandableListAdapter extends BaseExpandableListAdapter {

    private Context mContext;
    private List<String> mHeroCategory;
    private HashMap<String, List<String>> mHeroName;

    public MyExpandableListAdapter(Context context, List<String> expandableListTitle,
                                   HashMap<String, List<String>> expandableListDetail) {
        mContext = context;
        mHeroCategory = expandableListTitle;
        mHeroName = expandableListDetail;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return mHeroName.get(mHeroCategory.get(groupPosition)).get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition,
                             boolean isLastChild, View convertView, ViewGroup parent) {
        final String expandedListText = (String) getChild(groupPosition, childPosition);
        if (convertView == null) {
            LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = layoutInflater.inflate(R.layout.list_item, null);
        }
        TextView expandedListTextView = convertView.findViewById(R.id.expandedListItem);
        expandedListTextView.setText(expandedListText);
        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mHeroName.get(mHeroCategory.get(groupPosition)).size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return mHeroCategory.get(groupPosition);
    }

    @Override
    public int getGroupCount() {
        return mHeroCategory.size();
    }

    @Override
    public long getGroupId(int listPosition) {
        return listPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        String listTitle = (String) getGroup(groupPosition);
        if (convertView == null) {
            LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = layoutInflater.inflate(R.layout.list_group, null);
        }
        TextView listTitleTextView = convertView
                .findViewById(R.id.listTitle);
        listTitleTextView.setTypeface(null, Typeface.BOLD);
        listTitleTextView.setText(listTitle);
        return convertView;
    }

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

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

如果有对这些回调接口的实现不太理解的,可以回顾一下第24节中讲 ListView 的时候对 BaseAdapter 做的详细讲解。

3.5 编写 MainActivity

前面已经实现了布局、数据、适配器等模块的编写,整个 ExpandableListView 的框架就已经搭建完毕了。虽然本节的示例比较简单,代码量也比较少,但是也希望大家在学习过程中能够注重模块的编写顺序,循序渐进的培养自己搭建一个更完整的更大型架构的能力。
框架搭建完毕就可以进入业务代码的编写了,在MainActivity中我们主要做以下4件事:

  1. 设置布局文件并从布局文件中拿到 ExpandableListView 实例;
  2. 获取数据集(实际使用中可能是从网络获取或者本地读取);
  3. 创建适配器,并为 ExpandableListView 实例设置适配器;
  4. 为 ExpandableListView 添加相应的事件监听器,并实现监听器接口中的回调方法。

按照以上 4 步来做即可,代码如下:

package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MainActivity extends Activity {

    HashMap<String, List<String>> expandableListDetail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 1.设置布局文件并从布局文件中拿到 ExpandableListView 实例;
        setContentView(R.layout.activity_main);
        ExpandableListView listView = findViewById(R.id.expandableListView);
        // 2. 获取数据集(实际使用中可能是从网络获取或者本地读取)
        expandableListDetail = DataCollection.getData();
        final List<String> heroCategory = new ArrayList<>(expandableListDetail.keySet());
        // 3. 创建适配器,并为 ExpandableListView 实例设置适配器
        ExpandableListAdapter adapter = new MyExpandableListAdapter(this, heroCategory, expandableListDetail);
        listView.setAdapter(adapter);
        // 4. 为 ExpandableListView 添加相应的事件监听器,并实现监听器接口中的回调方法
        listView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {

            @Override
            public void onGroupExpand(int groupPosition) {
                Toast.makeText(getApplicationContext(), heroCategory.get(groupPosition)
                                + " 列表展开", Toast.LENGTH_SHORT).show();
            }
        });

        listView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {

            @Override
            public void onGroupCollapse(int groupPosition) {
                Toast.makeText(getApplicationContext(), heroCategory.get(groupPosition)
                                + " 列表折叠", Toast.LENGTH_SHORT).show();

            }
        });

        listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Toast.makeText(getApplicationContext(), heroCategory.get(groupPosition)
                        + " -> " + expandableListDetail.get(heroCategory.get(groupPosition))
                        .get(childPosition), Toast.LENGTH_SHORT
                ).show();
                return false;
            }
        });
    }
}

编译运行之后,界面上会展示一个 5 大英雄类别的 ListView,点击每个类别系统会回调onGroupExpand方法,我们在当中打印出当前被展开的类别名;然后会弹出该类下的英雄名称,点击英雄名称系统会回调onChildClick方法,我们在方法中打印出被点击的英雄名称;最后我们可以点击已经展开的英雄类别,系统会将点击的类别恢复折叠状态同时回调onGroupCollapse方法,在其中我们打印出被折叠的类别名称,最终效果如下:

4. 小结

本节学习了 ListView 的升级版,ExpandableListView 继承自 ListView,在 ListView 的基础之上加上了二级分类,所以引入了 group 的概念。在布局文件中除了正常的列表 item 外还需要有一个 group 的布局;

ExpandableListAdapter 也多了一些针对 group 的处理;数据也需要分主类别和子类别,我们先将英雄分为 5 大类,接着在 5 个大类下分别列举了一些该类的英雄名称,最终通过 ExpandableListAdapter 实现了一个英雄分类的示例 App。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1610050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css样式,经典老问题回顾

跑到了一堆来了 这时需要在父div加相对定位的css 传说中的 子绝父相

Python Flask Web框架快速入门

Flask 入门Demo Flask 开发环境搭建&#xff0c;执行如下指令&#xff1a; pip install flask # 第一节: Flask 快速入门from flask import Flask app Flask(__name__)app.route(/flask) def hello_flask():return Hello Flaskapp.run() 核心代码剖析&#xff1a; 从 fla…

微信小程序开发之多图片上传+.NET WebAPI后端服务保存图片资源

前言&#xff1a; 最近开发的一个微信小程序项目需要做一个同时选中三张&#xff08;或者是多张&#xff09;图片一起上传到服务端&#xff0c;服务端保存图片资源并保存的功能。发现在微信小程序开发中会有很多场景会使用到多图片上传并保存到的功能&#xff0c;所以我把自己总…

酷开科技抓住“客厅经济”发展的机遇,不断对酷开系统升级赋能

酷开科技抓住“客厅经济”发展的机遇&#xff0c;不断对酷开系统升级赋能&#xff0c;打造新的生活场景&#xff0c;满足消费者的不同生活需求&#xff0c;酷开科技的产品和服务让消费者能够在家庭空间中享受到更加智能、便捷和温馨的时光。同样凭借更加包容、开放的生态体验&a…

unordered_map 与map使用说明

目录 背景&#xff1a; 问题分析&#xff1a; 使用APE工具&#xff0c;查看录制的原始门信号是否存在异常 查看报文读取是否存在问题 分析报文读取代码 为什么在Windows系统中&#xff0c;解析后的门信号没有存在跳变情况 是否Windows 和Linux下 unordered_map中数据先后…

文本批量高效编辑管理,支持将文本进行自定义行数进行拆分,实现文本的高效管理

在信息爆炸的时代&#xff0c;文本文件的管理和编辑成为了许多工作和学习中不可或缺的一部分。面对大量的文本内容&#xff0c;如何高效地进行编辑和管理成为了一个挑战。现在&#xff0c;我们为您带来了一款强大的批量文本编辑管理工具&#xff0c;支持自定义行数拆分&#xf…

面试题集中营—分布式共识算法

分布式共识算法目标 分布式主要就是为了解决单点故障。一开始只有一个服务节点提供服务&#xff0c;如下图所示。那么如果服务节点挂了&#xff0c;对不起等着吧。 为了服务的高可用性&#xff0c;我们一般都会多引入几个副节点当备份&#xff0c;当服务节点挂了&#xff0c;就…

BRC20铭文铭刻解析

BRC20铭文铭刻的出现对于智能制造无疑是一个重要的里程碑。随着科技的飞速发展&#xff0c;智能制造已经成为制造业发展的必然趋势&#xff01;智能制造是指通过运用人工智能、物联网、大数据等先进技术&#xff0c;实现生产过程的自动化、智能化和高效化。 1. BRC20铭文的概念…

2024常见的自动化测试工具和框架!

在软件测试领域&#xff0c;自动化测试框架有很多&#xff0c;这里主要介绍几种常用的自动化测试框架。 以下是几种常用的自动化测试框架&#xff1a; Selenium&#xff1a;Selenium 是一个功能强大的Web应用程序测试框架&#xff0c;支持多种编程语言&#xff0c;如Java、Pyt…

在ELF 1开发环境中使用Qt Creator进行远程调试

Qt Creator是一款跨平台集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要适用于支持Qt框架的各类应用程序开发。其内置的远程调试机制使得开发者能够在本地开发环境中对部署在远程设备上的代码进行调试&#xff0c;无需直接对远程设备进行操作。Qt Creator会通过网络连…

Django中的实时通信:WebSockets与异步视图的结合

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…

将图片按灰度转换成字符

from struct import *ch [., :, !, ~, ,, ^, *,$, #]//可以在这里面由浅到深添加字符 ch.reverse()def calc(R, G, B):#模式Lreturn R * 299 // 1000 G * 587 // 1000 B * 144 / 1000def character( val):num val / 260 * len(ch)num round(num)if num>len(ch):numlen(…

【H4012】3.3V5V12V24V30V,3.5A大电流温度低 高效同步降压芯片IC DC-DC

您提到的“3.3V, 5V, 12V, 24V, 30V, 3.5A 高效同步降压芯片IC DC-DC”是指一种能够将较高电压&#xff08;例如24V或30V&#xff09;降至较低电压&#xff08;例如3.3V, 5V或12V&#xff09;的直流-直流&#xff08;DC-DC&#xff09;转换器。这种转换器通常使用同步降压技术&…

uniapp 安卓批量异步权限授权,没有授权就跳系统App设置页

首先需要一个js的sdk&#xff1a;App权限判断和提示 - DCloud 插件市场 下载下来&#xff0c;引入里面的 permission.js 示例代码&#xff1a; <script>import { requestAndroidPermission } from ./sdk/permission.jsexport default {onLaunch(e) {const getMutiPer…

kerberos:适配华为FI

文章目录 一、hive1、hive thrift连接方式 一、hive 1、hive thrift连接方式 kerberos认证失败信息 缺少配置&#xff1a;{“hadoop.rpc.protection”:“privacy”}&#xff0c;具体可参考&#xff1a;kerbros认证相关问题 华为FI参考资料&#xff1a; https://github.com…

从零开始学习Linux(4)----yum和vim

1.Linux软件包管理器yum Linux中我们要进行工具/指令/程序&#xff0c;安装&#xff0c;检查卸载等&#xff0c;需要yum的软件 安装软件的方式&#xff1a; 源代码安装---交叉编译的工具rpm包直接安装yum/apt-get yum是我们Linux预装的一个指令&#xff0c;搜索&#xff0c;下…

如何防止用户手动填写身份证信息来作弊?

当前有不少的网站或企业需要用户采用身份证读卡器远程在程序里面填写身份证信息&#xff0c;用于核实用户的真实身份&#xff0c;以完成某些业务&#xff0c;但是&#xff0c;有些用户为了达到不可告人的目的&#xff0c;会采用作弊手段&#xff0c;绕过机器采集这一关&#xf…

牛客网:环形链表的约瑟夫问题

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;每日一练 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f3dd;1.问题描述&#xff1a; 前言&#xff1a; 约瑟夫问题 有很多种解决办法&#xff0c;下面我们用链表进行解题 题目链…

学校管网的仿写

工字形布局完成 效果 代码部分 在这里插入代码片 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport…

AI日报:最强大模型Llama 3发布;Midjourney推社交新功能Room;超强AI视频自动剪辑工具Captions;手机上可以玩大模型了

新鲜AI产品点击了解&#xff1a;https://top.aibase.com/ 1、最强大模型Llama3 正式发布 已达GPT4 级别 Llama3是Meta公司最新发布的开源模型&#xff0c;拥有80亿和700亿参数规模&#xff0c;预计7月正式发布。该模型具备多模态能力&#xff0c;集成了新的计算机编码功能&am…