您的位置:首页 > 移动开发 > Android开发

Android中实现类似qq好友列表展开收起的效果

2016-04-29 10:28 811 查看
最近两天学习实现了一个功能,感觉很好,一定要记录下来



在网上找了一些资料,林林总总,总是不那么齐全,有的代码做成小Demo还会报错,需要自己调试半天。也幸好如此,我将此功能涉及到的一些知识点理解的更加深刻一些。

功能需求:设计一个列表,类似qq好友列表,点击一级标题(对应:组名称),展开二级内容(对应:好友列表),再点击,则收起二级内容。一级标题下有一按钮,随着列表的展开收起自动的跟在一级列表下。

该功能使用了Android中的一个控件:ExpandableListView。

ExpandableListView是ListView的子类,它在普通的ListView的基础上进行了拓展,它把应用中的列表分为几组,每组又包含多个列表项。它的用法和ListView很像,只是其所显示的列表项应该由ExpandableListAdapter提供。下面提供该控件一些属性说明:

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

android:childIndicator ———————————— 显示在子列表旁边的Drawable对象

android:childDivider ———————————— 指定各组内子类表项之间的分割条:图片不会完全显示,分离子列表项的是一条直线。

android:childIndicatorLeft —————————— 子列表项指示符的左边约束位置:即从左边0位置开始计数,比如,假设指示符是一个图标,给定这个属性值为3dp,即表示从左端3dp位置开始显示此图标。

android:childIndicatorRight —————————— 子列表项指示符的右边约束位置:表示左端从什么位置开始。

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

注意:在XML文件中,如果ExpandableListView上一级视图的大小没有严格定义的话,则不能对ExpandableListView的android:layout_height属性使用wrap_content值。(例如,如果上一级视图是ScrollView的话,则不应该指定wrap_content的值,因为它可以是任意的长度。不过,如果ExpandableListView的上一级视图有特定的大小的话,比如100像素,则可以使用wrap_content)。

适用于ExpandableListView的Adapter要继承BaseExpandableListAdapter,且必须重载getGroupView和getChildView两个最重要的方法。使用BaseExpandableListAdapter时,需要重载一下方法。

/**
* 取得分组数
*
* @return 组数
*/
@Override
public int getGroupCount() {
// TODO Auto-generated method stub
return 0;
}

/**
* 取得指定分组的子元素数
*
* @param groupPosition
*            :要取得子元素个数的分组位置
* @return:指定分组的子元素个数
*/
@Override
public int getChildrenCount(int groupPosition) {
// TODO Auto-generated method stub
return 0;
}

/**
* 取得与给定分组关联的数据
*
* @param groupPosition
*            分组的位置
* @return 指定分组的数据
*/
@Override
public Object getGroup(int groupPosition) {
// TODO Auto-generated method stub
return null;
}

/**
* 取得与指定分组、指定子项目关联的数据
*
* @param groupPosition
*            :包含子视图的分组的位置
* @param childPosition
*            :指定的分组中的子视图的位置
* @return 与子视图关联的数据
*/
@Override
public Object getChild(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return null;
}

/**
* 取得指定分组的ID.该组ID必须在组中是唯一的.必须不同于其他所有ID(分组及子项目的ID)
*
* @param groupPosition
*            要取得ID的分组位置
* @return 与分组关联的ID
*/
@Override
public long getGroupId(int groupPosition) {
// TODO Auto-generated method stub
return 0;
}

/**
* 取得给定分组中给定子视图的ID.该组ID必须在组中是唯一的.必须不同于其他所有ID(分组及子项目的ID)
*
* @param groupPosition
*            包含子视图的分组的位置
* @param childPosition
*            要取得ID的指定的分组中的子视图的位置
* @return 与子视图关联的ID
*
*/
@Override
public long getChildId(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return 0;
}

/**
* 是否指定分组视图及其子视图的id对应的后台数据改变也会保持该id
*
* @return 是否相同的id总是指向同一个对象
*/
@Override
public boolean hasStableIds() {
// TODO Auto-generated method stub
return false;
}

/**
* 去的用于显示给定分组的视图,该方法仅返回分组的视图对象。
*
* @param groupPosition
*            :决定返回哪个视图的组位置
* @param isExpanded
*            :该分组是展开状态(true)还是收起状态(false)
* @param convertView
*            :如果可能,重用旧的视图对象.使用前你应该保证视图对象为非空,并且是否是合适的类型.
*            如果该对象不能转换为可以正确显示数据的视图 ,该方法就创建新视图.不保证使用先前由getGroupView(int,
*            boolean,View, ViewGroup)创建的视图.
* @param parent
*            :该视图最终从属的父视图
*
* @return 指定位置相应的组试图
*
*/
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}

/**
* 取得显示给定分组给定子位置的数据用的视图
*
* @param groupPosition
*            :包含要取得子视图的分组位置。
* @param childPosition
*            :分组中子视图(要返回的视图)的位置。
* @param isLastChild
*            :该视图是否为组中的最后一个视图。
*
* @param convertView
*            : 如果可能,重用旧的视图对象,使用前应保证视图对象为非空,且是否是适合的类型。
*            如果该对象不能转换为正确显示数据的视图,该方法就创建新视图。
*            不保证使用先前由getChildView(int,int,boolean,View,ViewGroup)创建的视图。
* @param parent
*            :该视图最终从属的父视图
*
* @return:指定位置相应的子视图。
*/
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}

/**
* 指定位置的子视图是否可选择
*
* @param groupPosition
*            包含要取得子视图的分组位置
* @param childPosition
*            分组中子视图的位置
* @return 是否子视图可选择
*/
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return false;
}
基础知识已经总结的差不多了,话不多说,直接上代码!!!!!!

下面我将我的部分源代码附上。

我的这个列表是显示在一个fragment中的,如果显示在activity中将会更简单。

1. fragment_cars_list.xml

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

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

<TextView
android:id="@+id/tv_load_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/android_list"
android:layout_centerHorizontal="true"
android:padding="20dp"
android:text="@string/load_more" />

</RelativeLayout>
2. group.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="match_parent"
android:orientation="vertical" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<ImageView
android:id="@+id/iv_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp" />

<TextView
android:id="@+id/tv_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="6dp"
android:paddingLeft="10dp"
android:paddingTop="6dp"
android:text="@string/list"
android:textSize="15sp" />
</LinearLayout>

</LinearLayout>
3. child.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="match_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/tv_child"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="10dp"
android:paddingLeft="60dp"
android:paddingTop="10dp"
android:text="好友列表"
android:textSize="20sp" />

</LinearLayout>
4. BuddyAdapter.java

public class BuddyAdapter extends BaseExpandableListAdapter {

private String[] group;
private String[] buddy;
private Context context;
private LayoutInflater inflater;

public BuddyAdapter(String[] group, String[] buddy, Context context) {
super();
this.group = group;
this.buddy = buddy;
this.context = context;
inflater = LayoutInflater.from(context);
}

@Override
public int getGroupCount() {
return group.length;
}

@Override
public int getChildrenCount(int groupPosition) {
return buddy.length;
}

@Override
public Object getGroup(int groupPosition) {
return group[groupPosition];
}

@Override
public Object getChild(int groupPosition, int childPosition) {

return buddy[childPosition];
}

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

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

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

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
convertView = inflater.inflate(R.layout.group, null);
TextView groupNameTextView = (TextView) convertView
.findViewById(R.id.tv_group);
ImageView ivSelector = (ImageView) convertView
.findViewById(R.id.iv_selector);
groupNameTextView.setText(getGroup(groupPosition).toString());
ivSelector.setImageResource(R.drawable.selector_close);

// 更换展开分组图片
if (!isExpanded) {
ivSelector.setImageResource(R.drawable.selector);
}
return convertView;
}

@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
convertView = inflater.inflate(R.layout.child, null);
TextView nickTextView = (TextView) convertView
.findViewById(R.id.tv_child);

nickTextView.setText(getChild(groupPosition, childPosition).toString());

return convertView;
}

// 子选项是否可以选择
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}

}
4. carListFragment.java

public class CarsListFragment extends Fragment {

private ExpandableListView elvCompany;

private TextView tvLoadMore;

// 群组名称(一级条目内容)
private String[] group = new String[] { "我的好友" };

private String[] carsList = new String[] { "张三", "李四", "王五", "赵六",
"天气" };

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_cars_list, null);

tvLoadMore = (TextView) view.findViewById(R.id.tv_load_more);
elvCompany = (ExpandableListView) view.findViewById(R.id.android_list);
BuddyAdapter adapter = new BuddyAdapter(group, carsList, getContext());
elvCompany.setAdapter(adapter);

setListeners();

return view;
}

private void setListeners() {
// 分组展开
elvCompany.setOnGroupClickListener(new OnGroupClickListener() {

@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
return false;
}
});
// 分组关闭
elvCompany.setOnGroupCollapseListener(new OnGroupCollapseListener() {

@Override
public void onGroupCollapse(int groupPosition) {

}
});

// 子项点击
elvCompany.setOnChildClickListener(new OnChildClickListener() {

@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Toast.makeText(getActivity(),
group[groupPosition] + ":" + carsList[childPosition],
Toast.LENGTH_SHORT).show();
return false;
}
});

tvLoadMore.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "没有更多数据了", Toast.LENGTH_SHORT)
.show();

}
});

}

}
运行之后,功能实现,但是会发现一个问题,在组列表左侧有一个默认的图标,如图所示:



那么我现在不要这个默认的图标使用我自己的图标,怎么设置呢?

其实很简单很简单,只需要设置一个属性值即可实现。我在fragment_cars_list.xml文件中的ExpandableListView控件设置属性android:groupIndicator="@null"即可实现效果。

该功能的实现特别感谢一篇文章的作者:

http://www.open-open.com/lib/view/open1406014566679.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: