zoukankan      html  css  js  c++  java
  • Android开发 ExpandableListView 可折叠列表详解

    前言

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

    一个简单的Demo

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

    效果图

      

    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

  • 相关阅读:
    超详细教程2021新版oracle官网下载Windows JAVA-jdk11并安装配置(其他版本流程相同)
    个人总结
    6.15 团队项目心得
    五月团队项目收获
    八大排序算法读书笔记
    设计模式读书笔记3
    设计模式读书笔记2
    结对编程收获
    设计模式读书笔记
    UI-12组结对编程作业总结
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/11294396.html
Copyright © 2011-2022 走看看