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