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

时间:2022-11-08 19:33:42

最近两天学习实现了一个功能,感觉很好,一定要记录下来Android中实现类似qq好友列表展开收起的效果

在网上找了一些资料,林林总总,总是不那么齐全,有的代码做成小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();

			}
		});

	}

}
运行之后,功能实现,但是会发现一个问题,在组列表左侧有一个默认的图标,如图所示:
Android中实现类似qq好友列表展开收起的效果

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

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



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

http://www.open-open.com/lib/view/open1406014566679.html