Android开发 ExpandableListView 可折叠列表详解

摘要:
<xmlversion=“1.0”encoding=“utf-8”;&lt?&lt:>列表<>mChildList=子列表;
前言

  在需要实现一个List的item需要包含列表的时候,我们就可以选择ExpandableListView. 其实这个View的原始设计还是ListView的那套.就是增加2层的ListView而已.所以在写它的适配器与ListView的适配器挺相似的,所以会有一个通病就是没有Item的View的复用机制请一定要注意这点,在实现使用的时候需要写Item的View的复用,减少内存与增加性能.

一个简单的Demo

  老规矩,先来一个最简单的demo来了解下最基本的使用方法.注意!这个demo是没有在Adapter写任何View复用机制的请不要用到实际项目中. demo只是帮助你快速认识了解ExpandableListView

效果图

Android开发 ExpandableListView 可折叠列表详解第1张  

Activity的Xml

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

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

</LinearLayout>

一级Item和二级Item用的xml布局

偷懒,我让一级和二级都使用一个布局

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text1"
    android:layout_width="match_parent"
    android:layout_height="45dp"
    android:text="内容"
    android:textSize="15sp"
    android:textColor="@color/fontBlack3"
    android:gravity="center"
    android:background="@color/colorWhite">
</TextView>

编写适配器

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import java.util.List;

public class DemoAdapter extends BaseExpandableListAdapter {
    List<String> mGroupList;//一级List
    List<List<String>> mChildList;//二级List 注意!这里是List里面套了一个List<String>,实际项目你可以写一个pojo类来管理2层数据


    public DemoAdapter(List<String> groupList, List<List<String>> childList){
        mGroupList = groupList;
        mChildList = childList;

    }

    @Override
    public int getGroupCount() {//返回第一级List长度
        return mGroupList.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {//返回指定groupPosition的第二级List长度
        return mChildList.get(groupPosition).size();
    }

    @Override
    public Object getGroup(int groupPosition) {//返回一级List里的内容
        return mGroupList.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {//返回二级List的内容
        return mChildList.get(groupPosition).get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {//返回一级View的id 保证id唯一
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {//返回二级View的id 保证id唯一
        return groupPosition + childPosition;
    }

    /**
     * 指示在对基础数据进行更改时子ID和组ID是否稳定
     * @return
     */
    @Override
    public boolean hasStableIds() {
        return true;
    }

    /**
     *  返回一级父View
     */
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_item, parent,false);
        ((TextView)convertView).setText((String)getGroup(groupPosition));
        return convertView;
    }

    /**
     *  返回二级子View
     */
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_item, parent,false);
        ((TextView)convertView).setText((String)getChild(groupPosition,childPosition));
        return convertView;
    }

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

Activity里的代码

        mExpandableListView = findViewById(R.id.expandablelistview);

        List<String> groupList = new ArrayList<>();
        groupList.add("一");
        groupList.add("二");
        groupList.add("三");

        List<List<String>> childList = new ArrayList<>();
        List<String> childList1 = new ArrayList<>();
        childList1.add("1");
        childList1.add("1");
        childList1.add("1");
        List<String> childList2 = new ArrayList<>();
        childList2.add("2");
        childList2.add("2");
        childList2.add("2");
        List<String> childList3 = new ArrayList<>();
        childList3.add("3");
        childList3.add("3");
        childList3.add("3");

        childList.add(childList1);
        childList.add(childList2);
        childList.add(childList3);

        DemoAdapter demoAdapter = new DemoAdapter(groupList, childList);
        mExpandableListView.setAdapter(demoAdapter);

        mExpandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {//一级点击监听
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {

                //如果你处理了并且消费了点击返回true,这是一个基本的防止onTouch事件向下或者向上传递的返回机制
                return false;
            }
        });

        mExpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {//二级点击监听
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {

                //如果你处理了并且消费了点击返回true
                return false;
            }
        });
其他Xml属性

android:dividerHeight="20dp" 设置item间距高度,注意设置这个间距包括了一级和二级

android:divider="@color/colorRed1" 设置一级间距颜色

android:childDivider="@color/colorGreen" 设置二级间距颜色

android:childIndicator:显示在子列表旁边的Drawable对象,可以是一个图像

android:childIndicatorEnd:子列表项指示符的结束约束位置

android:childIndicatorLeft:子列表项指示符的左边约束位置

android:childIndicatorRight:子列表项指示符的右边约束位置

android:childIndicatorStart:子列表项指示符的开始约束位置

android:groupIndicator:显示在组列表旁边的Drawable对象,可以是一个图像

android:indicatorEnd:组列表项指示器的结束约束位置

android:indicatorLeft:组列表项指示器的左边约束位置

android:indicatorRight:组列表项指示器的右边约束位置

android:indicatorStart:组列表项指示器的开始约束位置

可以实现的ExpandableListView3种Adapter

1. 扩展BaseExpandableListAdpter实现ExpandableAdapter。

2. 使用SimpleExpandableListAdpater将两个List集合包装成ExpandableAdapter

3. 使用simpleCursorTreeAdapter

ExpandableListView的一些API详解

mExpandableListView.collapseGroup(position);   收起指定位置组的二级列表

mExpandableListView.expandGroup(position);  展开指定位置组的二级列表

mExpandableListView.isGroupExpanded(position);  指定位置的组是否展开

mExpandableListView.setSelectedGroup(position);  将指定位置的组设置为置顶

改变方向图标的位置
int width = getResources().getDisplayMetrics().widthPixels;
mExpandableListView.setIndicatorBounds(width - UnitConversionUtil.dip2px(this,40)
                , width - UnitConversionUtil.dip2px(this,15));//设置图标位置
关于点击事件的一些坑

  如果你在适配器里去实现了Group的点击事件想用回调方法回调出去(如下代码),这个时候你就会碰到一个Group无法展开和收起的坑,原因很简单因为这里已经把点击事件消费了,点击不在继续向下传递,所以底层实现的展开和收起不执行了

  /**
     *  返回一级父View
     */
    @Override
    public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, final ViewGroup parent) {
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            convertView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                }
            });
        return convertView;
    }

那么如何解决呢?

方法一

  不用setOnClickListener(),因为这个会消费事件,我们改用setOnTouchListener,如下代码:

      convertView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_DOWN){
                  //实现你自己的接口回调
                    }
                    return false;
                }
            });        

  这里有一点需要注意ACTION_DOWN 需要返回false,因为底层是消费ACTION_DOWN的来展开和收起的....

方式二

将groupPosition回调到外面后使用collapseGroup() 或者 expandGroup()方法实现.

end

免责声明:文章转载自《Android开发 ExpandableListView 可折叠列表详解》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇pdf.js 前端pdf预览 渲染文本图层支持复制 保证手机端清晰度 双指缩放 alloyfinger(手势)使用docx4j实现docx转pdf(解决linux环境下中文乱码问题)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android二维码功能实现

最近二维码真是越来越火了,随便电视上、网络上、商场里,到处都是二维码。而内嵌二维码扫描功能的软件也越来越多,QQ、微信、UC浏览器等等应用都可以对着二维码扫一扫,感觉我们自己的应用里不加上二维码扫描功能,都跟不上时代潮流了。所以今天我就将带着大家一起,在我们自己的程序里加入二维码扫描的功能。 不过,二维码功能如果真要做起来还是非常复杂的,从零开始实现不太现...

Quartz.NET文档 入门教程

概述 Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。 Quartz.NET允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz.NET的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业...

用jq中jSignature做手动签名

<!DOCTYPE html><html lang="zh-CN"><head> <title>手写板签名demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <...

Android 修改 keystore 信息

在进行 开放sdk接入的时候,比如微信sdk,就需要 对apk进行签名才能调用 频繁打包很繁琐,且不能调试,这就需要对debug包进行签名, 而eclipse等工具是用默认 密钥文件 的别名和密码进行签名的(居然不能自定义!!!),而我们自己的密钥文件一般跟ide的默认名字不一致,这就需要我们将debug.keystore 修改为eclipse的默认 别名...

用C++写android程序(包含界面+发短信)

首先为什么要用C++写android程序呢?主要是因为java写的android程序太容易被发编译,相对于java编译后的dex文件,底层的native so更加不容易被反编译,所以为了安全起见,可以将一些程序的逻辑写到C++里面,下面我们就写一个activity,其中核心内容都写在C++里面。我们写一个演示界面,其中后台发一条短信(所有逻辑都在C++中)...

APK反编译去广告大揭秘

APK反编译去广告具体步骤:1.下载 apktool  下载地址:https://code.google.com/p/android-apktool/downloads/list 2.通过apktool 反编译apk。命令行中输入以下命令:apktool.bat  d  -f   aaa.apk(apk路径) 反编译APK文件 3.开始修改XML布局文件...