ExpandableListView的使用和理解

ExpandableListView顾英文思意:可扩展列表视图。

之前一直看别人用这个控件,哇,觉得肯定很复杂,数据也很复杂吧,也就一直没尝试用到这个控件,就和我对自定义View是一样的感觉,是新手必经之路,又很怕去触碰,觉得比较难,放在以后再说,但是,建议,没有什么难不难,去理解,去写控件的代码,走一遍,想想,就理解了。言归正传:

ExpandableListView 就是ListView的进化版,满足了一般ListView做不到的事情,如现在的QQ好友列表的样式,点击拉伸扩展,再点击缩小,着就是父item和子item的关系了。

首先,先看看主布局:activity_main.xml:

   <ExpandableListView
        android:id="@+id/expanda_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="#000000" />

仅此而已,外层你可以用LinearLayout也好,或者是最新的ConstraintLayout布局(约束布局)也好等等,都可以。

其次要需要创建两个xml布局,一个是父Item,一个是子Item,所有的只需要这两个布局去实现,因为我们有Holder呀,一个这么神奇复用的东西对吧~
父布局.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:orientation="horizontal">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="70dp">

        <ImageView
            android:id="@+id/img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp" />

        <TextView
            android:id="@+id/txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:layout_toRightOf="@+id/img"
            android:text="张三"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/txt2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="20dp"
            android:gravity="right"
            android:text="4/17" />
    </RelativeLayout>


</LinearLayout>

子布局xml:.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:padding="3dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/img"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginLeft="20dp"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:id="@+id/txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="皮皮护法" />

        <TextView
            android:id="@+id/txt2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="哇,皮皮虾,我们走我们走" />
    </LinearLayout>


</LinearLayout>

不要觉得看起来很多,这只是为了效果,其实很多都是没有意义的属性,比如有几个textview就不会用到他,只是让他显示text而已。

一般默认的ExpandableListView的父Item都是有小箭头的,可以理解为打开与不打开有个小三角形的指示图标。

在MainActivity.java中,类似的与ListView一样,需要放入数据,需要声明这个ExpandableListView控件,还需要一个适配器,以及两个复用的Holder就足够了。
我的代码有点乱,但是记住两个核心:
1. 一个List group = new ArrayList<>();这样的List是专门为父Item提供的,需要什么样的栏目,直接将数据放进该List即可。
2. 一个List《List《String》》 item_list =new ArrayList<>();这样的
List《List《String》> 是存放每个子Item相对应的数据,可以知道,item_list存放进去的是一个list<>对象,而在list对象中在放入相对应的String的数据,这个层级关系一定要理清楚。

下面是代码:

public class MainActivity extends AppCompatActivity {

    private ExpandableListView expandableListView; //expandablelistview控件
    private MyExpandableListViewAdapter adapter;   //适配器

    private List<String> group_list = new ArrayList<>();//父item 显示的东西,加数据就多现实一栏父item 父列表

    private List<String> text = new ArrayList<>();  //子item对应的文字集合
    private List<Integer> tupian = new ArrayList<>();   //子item对应的图片集合

    private List<List<String>> item_list = new ArrayList<>();//将子item放进来,适配进每个父Item的相对应子item
    private List<List<Integer>> item_list2 = new ArrayList<>();//将子item图片放进来,适配进每个父item相对应的子item

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

        /**
         * 父item的栏目,多一行则多显示一行的界面数据
         */
        group_list.add("我的好友");
        group_list.add("我的亲人");
        group_list.add("我的同学");
        group_list.add("我的兄弟");

        /**
         * 关于子item的数量,文字显示
         */
        text.add("皮皮虾");
        text.add("石乐志");
        text.add("真皮");
        text.add("石乐志");
        text.add("真皮");
        text.add("石乐志");
        /**
         * 这一栏负责子item的图片显示, 所有的东西都是同步,改变12行,每个子item 1.2行都会变
         */
        tupian.add(R.drawable.two);
        tupian.add(R.drawable.two);
        tupian.add(R.drawable.two);
        tupian.add(R.drawable.two);
        tupian.add(R.drawable.two);
        tupian.add(R.drawable.two);
        item_list2.add(tupian);
        item_list2.add(tupian);
        item_list2.add(tupian);
        item_list2.add(tupian);
        item_list2.add(tupian);
        item_list2.add(tupian);
        /**
         * 子Item的文字。和图片必须同时增加,不然就会指数越界,切记。两个同时从4行加到6行才行
         */

        /**
         * 决定着多少个父item能被打开,加入有四个父item,但是只写三行下面的代码,。第四个父item就会指数越界
         */
        item_list.add(text);
        item_list.add(text);
        item_list.add(text);
        item_list.add(text);

        expandableListView = (ExpandableListView) findViewById(R.id.expanda_list);//找该控件
        expandableListView.setGroupIndicator(null);

        /**
         * 可用lambda表达式简化,一般是需要写出来的,用lambda可省略
         * 对父子item的点击操作都可以写在这里面的逻辑中
         */
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {
                if (item_list.get(i).isEmpty()) {
                    return true;
                }
                return false;
            }
        });
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {
                Toast.makeText(MainActivity.this, "group=" + i + "child=" + i1, Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        adapter = new MyExpandableListViewAdapter(this);
        expandableListView.setAdapter(adapter);


    }

    private class MyExpandableListViewAdapter extends BaseExpandableListAdapter {

        private Context mContext;

        public MyExpandableListViewAdapter(Context context) {
            this.mContext = context;
        }

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

        @Override
        public int getChildrenCount(int i) {
            return item_list.get(i).size();
        }

        @Override
        public Object getGroup(int i) {
            return group_list.get(i);
        }

        @Override
        public Object getChild(int i, int i1) {
            return item_list.get(i).get(i1);
        }

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

        @Override
        public long getChildId(int i, int i1) {
            return i1;
        }

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

        /**
         *获得父布局的逻辑操作
         */
        @Override
        public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
            GroupHolder groupHolder = null;
            if (view == null) {
                view = LayoutInflater.from(mContext).inflate(R.layout.expendlist_group, null);
                groupHolder = new GroupHolder();
                groupHolder.txt = (TextView) view.findViewById(R.id.txt);
                groupHolder.img = (ImageView) view.findViewById(R.id.img);
                view.setTag(groupHolder);
            } else {
                groupHolder = (GroupHolder) view.getTag();
            }

            /**
             * 在这里 !b表示没有被父item没有被点击。前面的横向指针图片不会变
             * 如果被点击,则进入else逻辑,更换指针向下的图片
             */
        /*    if (!b) {
                groupHolder.img.setBackgroundResource(R.drawable.two);
            } else {
                groupHolder.img.setBackgroundResource(R.drawable.ic_launcher);
            }*/

            groupHolder.txt.setText(group_list.get(i));

            return view;
        }

        /**
         * 获得子布局的逻辑
         */
        @Override
        public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {
            ItemHolder itemHolder = null;
            if (view == null) {
                view = LayoutInflater.from(mContext).inflate(R.layout.expendlist_item, null);
                itemHolder = new ItemHolder();
                itemHolder.txt = (TextView) view.findViewById(R.id.txt);
                itemHolder.img = (ImageView) view.findViewById(R.id.img);
                view.setTag(itemHolder);
            } else {
                itemHolder = (ItemHolder) view.getTag();
            }
            itemHolder.txt.setText(item_list.get(i).get(i1));
            itemHolder.img.setBackgroundResource(item_list2.get(i).get(i1));
            return view;
        }

        @Override
        public boolean isChildSelectable(int i, int i1) {
            return true;
        }
    }

    private class GroupHolder {
        TextView txt;
        ImageView img;
    }

    private class ItemHolder {
        TextView txt;
        ImageView img;
    }
}

接下来是效果图:
这里写图片描述
这里写图片描述
我相信,代码的注释也都很清楚了,当然,不要忘记最后两个Holder,一个是对应获得父View的Holder,一个是对应子View的Holder,有所区别。剩下主要的也就是adapter的代码,无须担心,继承了BaseExpandableListAdapter之后AS会提示让你倒入这些方法,再去写就可以了。
对于上面的增加数据,我是把它写死了,那么就要注意,当增加一个子item的Text的时候,一定要增加一个相对应的子Image,因为text 和 image在我的布局是相对应一起的,如果只加一个而不管增加相对应的一个text或者image的话,就会数组越界异常,导致程序崩溃。

如果你能看到这里,我们就继续解释一下数据,上面的代码是将数据写死,如果需要时事获得数据呢?也很简单,通过从服务器获得的数据,将父item数据放入List<>中,将子Item的数据,依次add放入List<>中,在将该List<> 再次add进add进List《List》就可以了。其他的适配过程都一样。

当然,还有一种思路,这样的ExpandableListView看起来,是不是就和LinearLayout相似,是的,这样就接触到了自定义View有关的知识,自己写个Layout继承LinearLayout,然后抓取父Item 子Item的布局,通过View view = LayoutInflater.from(context).inflate(R.layout.child_layout)来抓取布局,然后自定义Layout.addView(view)是不是就行了呢。

这是粗略的一个自定义模拟ExpandableListView,可以直接从as 的 open打开,具体有想法的可以去琢磨琢磨改一改,还是挺有意思的。
http://download.csdn.net/detail/chenxuanhe1995/9787506

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值