标签:android expandablelistview it-xiao小巫
本节课介绍Android中可实现二级可展开收缩列表的ExpandableListView容器,笔者感觉它非常难用并且难理解,很多时候我们可能需要对控件进行扩展和定制,然而它不太方便扩展,它使用难点主要在数据结构上和对控件的事件监听,其他的实现方式类似ListView,下面会提供笔者在实际开发中使用到的案例。
上面实现的效果可展开的二级列表,每个组项都可能有若干个子项,默认的ExpandableListView不太美观,我们需要通过自定义布局类美化它,在使用过程中有一些需要我们去了解的点,会在实现过程提一下。
源码已经上传到Git@OSC,各位可以clone参考
http://git.oschina.net/devilwwj/AndroidDevelopCourse/tree/master/code?dir=1&filepath=code&oid=7961fb146029fb10776b101b918c59ff77fbd672&sha=e65897b0a246924292356f2b488d430c081081ff
布局分为:
- Activity布局
- 组项布局(layout_expand_group.xml)
- 子项布局(layout_expand_item.xml)
layout/activity_expandablelistview.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">
<ExpandableListView
android:id="@+id/expandablelistview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@color/transparent"
android:childDivider="@color/transparent"
android:fastScrollEnabled="true"
android:groupIndicator="@color/transparent"
android:divider="@null"
android:listSelector="@color/transparent"
android:choiceMode="singleChoice"
android:scrollbars="none" >
</ExpandableListView>
</LinearLayout>
layout/layout_expand_group.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/group_layout"
android:layout_width="match_parent"
android:layout_height="57dp"
android:background="#ffffff"
android:clickable="true"
android:minHeight="?android:attr/listPreferredItemHeight" >
<ImageView
android:id="@+id/iv_group_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="18dp"
android:background="@drawable/ic_leftnav_10" />
<TextView
android:id="@+id/tv_group_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="59dp"
android:text="时局"
android:textColor="#333333"
android:textSize="16sp" />
<View
android:id="@+id/item_group_devider"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignLeft="@+id/tv_group_text"
android:layout_alignParentBottom="true"
android:background="@color/listview_divider"
android:layout_marginRight="10dp"
android:visibility="visible"/>
<ImageView
android:id="@+id/iv_expand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="25dp"
android:clickable="true"
android:padding="5dp"
android:src="@drawable/ic_leftnav_down" />
</RelativeLayout>
layout/layout_expand_item.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="40dp"
android:background="@drawable/slidingmenu_item_selector"
android:minHeight="?android:attr/listPreferredItemHeight" >
<!-- android:color="@drawable/item_selected_text_color"/> -->
<TextView
android:id="@+id/tv_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="75dp"
android:text="时局"
android:textColor="@drawable/slidingmenu_item_text_selector"
android:textSize="15sp"
android:clickable="true"
/>
<View
android:id="@+id/item_devider"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="59dp"
android:layout_marginRight="10dp"
android:background="@color/listview_divider"
android:visibility="visible" />
</RelativeLayout>
自定义Adapter
- 继承BaseExpandableListAdapter并实现以下方法
- getGroupCount(获取组项的个数)
- getChildrenCount(获取子项个数)
- getGroup(获取组对象)
- getChild(获取子对象)
- getGroupId(获取组项id)
- getChildId(获取子项id)
- hasStableIds(组和子元素是否持有稳定的ID)
- getGroupView(获取显示指定组的视图对象)
- getChildView(获取显示指定项的视图对象)
- isChildSelectable(子项是否可选中)
- 传入组项列表(如:List<GroupItem>
)
- 传入子项列表(如:List<List<Category>>
)
适配器代码:
com.devilwwj.androiddevelopcourse.adapters.ExpandableListViewAdapter
package com.devilwwj.androiddevelopcourse.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.devilwwj.androiddevelopcourse.R;
import com.devilwwj.androiddevelopcourse.domain.Category;
import com.devilwwj.androiddevelopcourse.domain.GroupItem;
import java.util.HashMap;
import java.util.List;
/**
* 自定义可展开列表的适配器
*/
public class ExpandableListViewAdapter extends BaseExpandableListAdapter {
private Context mContext;// 上下文
private ExpandableListView expandableListView; // 可展开列表对象
private List<GroupItem> groupList; // 组列表
private List<List<Category>> childList; // 子项列表
private LayoutInflater inflater;
// 自定义接口回调监听器
private OnGroupExpandListener OnGroupExpandListener;
private OnGroupClickListener onGroupClickListener;
private OnChildItemClickListener onChildClickListener;
private HashMap<Integer, Boolean> maps = new HashMap<Integer, Boolean>();
private boolean expandStateAtPosition = false;
public OnGroupClickListener getOnGroupClickListener() {
return onGroupClickListener;
}
public void setOnGroupClickListener(
OnGroupClickListener onGroupClickListener) {
this.onGroupClickListener = onGroupClickListener;
}
public OnGroupExpandListener getOnGroupExpandListener() {
return OnGroupExpandListener;
}
public void setOnGroupExpandListener(
OnGroupExpandListener onGroupExpandListener) {
OnGroupExpandListener = onGroupExpandListener;
}
public OnChildItemClickListener getOnChildClickListener() {
return onChildClickListener;
}
public void setOnChildClickListener(
OnChildItemClickListener onChildClickListener) {
this.onChildClickListener = onChildClickListener;
}
private int mExpandedGroupPosition;
public int getExpandedGroupPosition() {
return mExpandedGroupPosition;
}
public void setExpandedGroupPosition(int mExpandedGroupPosition) {
this.mExpandedGroupPosition = mExpandedGroupPosition;
}
public ExpandableListViewAdapter(Context context,
ExpandableListView expandableListView, List<GroupItem> groupList,
List<List<Category>> childList) {
super();
this.mContext = context;
this.expandableListView = expandableListView;
this.groupList = groupList;
this.childList = childList;
inflater = LayoutInflater.from(context);
// 初始化列表展开状态
for (int i = 0; i < groupList.size(); i++) {
maps.put(i, false);
}
}
private int mGroupPosition = 0;
private int mChildPosition = 0;
/**
* 设置子项被选中方法
*
* @param groupPosition
* @param childPosition
*/
public void setItemChecked(int groupPosition, int childPosition) {
if (expandableListView == null) {
return;
}
this.mGroupPosition = groupPosition;
this.mChildPosition = childPosition;
int numberOfGroupThatIsOpened = 0;
for (int i = 0; i < groupPosition; i++) {
if (expandableListView.isGroupExpanded(i)) {
numberOfGroupThatIsOpened += this.getChildrenCount(i);
}
}
int position = numberOfGroupThatIsOpened + groupPosition
+ childPosition + 1;
if (!expandableListView.isItemChecked(position)) {
expandableListView.setItemChecked(position, true);
}
}
@Override
public int getGroupCount() {
return groupList.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return childList.get(groupPosition).size();
}
/*
* 获得组项 (non-Javadoc)
*
* @see android.widget.ExpandableListAdapter#getGroup(int)
*/
@Override
public GroupItem getGroup(int groupPosition) {
return groupList.get(groupPosition);
}
@Override
public Category getChild(int groupPosition, int childPosition) {
return childList.get(groupPosition).get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(final int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
final GroupViewHolder groupViewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.layout_expand_group, parent,
false);
groupViewHolder = new GroupViewHolder(convertView);
convertView.setTag(groupViewHolder);
} else {
groupViewHolder = (GroupViewHolder) convertView.getTag();
}
GroupItem groupItem = groupList.get(groupPosition);
groupViewHolder.itemGroupIcon.setBackgroundResource(groupItem
.getDrawableId());
groupViewHolder.itemGroupText.setText(groupItem.getText());
// 如果该组没有子项,则不显示箭头
if (childList.get(groupPosition).size() == 0) {
groupViewHolder.itemArrow.setVisibility(View.GONE);
groupViewHolder.itemGroupLayout
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onGroupClickListener.onGroupClick(groupPosition);
}
});
} else {
groupViewHolder.itemArrow.setVisibility(View.VISIBLE);
groupViewHolder.itemGroupLayout
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
OnGroupExpandListener.onGroupExpand(groupPosition);
}
});
}
groupViewHolder.itemGroupText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onGroupClickListener.onGroupClick(groupPosition);
}
});
groupViewHolder.itemArrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
OnGroupExpandListener.onGroupExpand(groupPosition);
}
});
// 判断isExpanded就可以控制按下还是关闭,同时更换图片,这里使用属性动画来控制旋转
if (isExpanded) {
groupViewHolder.itemArrow
.setImageResource(R.drawable.ic_leftnav_up);
// 没有孩子项就不隐藏分割线
if (childList.get(groupPosition).size() > 0) {
groupViewHolder.itemDivider.setVisibility(View.INVISIBLE);
} else {
groupViewHolder.itemDivider.setVisibility(View.VISIBLE);
}
} else {
groupViewHolder.itemArrow
.setImageResource(R.drawable.ic_leftnav_down);
groupViewHolder.itemDivider.setVisibility(View.VISIBLE);
}
return convertView;
}
@Override
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final ChildViewHolder childViewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.layout_expand_item,
parent, false);
childViewHolder = new ChildViewHolder(convertView);
convertView.setTag(childViewHolder);
} else {
childViewHolder = (ChildViewHolder) convertView.getTag();
}
String content = childList.get(groupPosition).get(childPosition)
.getTitle();
// 设置内容
childViewHolder.itemChildText.setText(content);
// 设置文本点击事件
childViewHolder.itemChildText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onChildClickListener.onChildItemClick(groupPosition,
childPosition);
}
});
if (childPosition == childList.get(groupPosition).size() - 1) {
childViewHolder.itemDivider.setVisibility(View.VISIBLE);
} else {
childViewHolder.itemDivider.setVisibility(View.GONE);
}
// 设置子项被选中的状态
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
// 设置孩子项可选中
return true;
}
private static class GroupViewHolder {
RelativeLayout itemGroupLayout;
ImageView itemGroupIcon;
TextView itemGroupText;
ImageView itemArrow;
View itemDivider;
public GroupViewHolder(View convertView) {
itemGroupLayout = (RelativeLayout) convertView
.findViewById(R.id.group_layout);
itemGroupIcon = (ImageView) convertView
.findViewById(R.id.iv_group_icon);
itemGroupText = (TextView) convertView
.findViewById(R.id.tv_group_text);
itemArrow = (ImageView) convertView.findViewById(R.id.iv_expand);
itemDivider = (View) convertView
.findViewById(R.id.item_group_devider);
}
}
private static class ChildViewHolder {
TextView itemChildText;
View itemDivider;
public ChildViewHolder(View convertView) {
itemChildText = (TextView) convertView
.findViewById(R.id.tv_item_text);
itemDivider = (View) convertView.findViewById(R.id.item_devider);
}
}
public interface OnGroupExpandListener {
void onGroupExpand(int position);
}
public interface OnGroupClickListener {
void onGroupClick(int position);
}
public interface OnChildItemClickListener {
void onChildItemClick(int groupPosition, int childPosition);
}
public boolean getExpandStateAtPosition(int groupPosition) {
// 获得当前位置的展开状态
expandStateAtPosition = maps.get(groupPosition).booleanValue();
return expandStateAtPosition;
}
public void setExpandStateAtPosition(int groupPosition,
boolean expandStateAtPosition) {
this.expandStateAtPosition = expandStateAtPosition;
maps.put(groupPosition, expandStateAtPosition);
}
}
解析一下上面的代码,我们可以看到ExpandableListView除了一个组项,每个组项下面有若干个子项,我们在使用的时候首先要确定要展示的数据结构,组项有groupPosition来标识位置,然而子项需要根据groupPosition和ChildPosition来标识位置,我们设置数据的时候分别在getGroupView和getChildView方法来设置组视图和子项视图数据,最后返回填充数据的视图对象,一些逻辑控制的代码也是在这两个方法中进行,比如控制组项的展开、组项的点击、子项的点击、子项被选中效果等等,这里笔者是自定义了回调接口来满足业务的需求,Android API也提供的类似的方法,大家可以查看官方文档。
Activity代码
com.devilwwj.androiddevelopcourse.activities.ExpandableListViewTestActivity
package com.devilwwj.androiddevelopcourse.activities;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.ExpandableListView;
import com.devilwwj.androiddevelopcourse.R;
import com.devilwwj.androiddevelopcourse.adapters.ExpandableListViewAdapter;
import com.devilwwj.androiddevelopcourse.domain.Category;
import com.devilwwj.androiddevelopcourse.domain.GroupItem;
import com.devilwwj.androiddevelopcourse.utils.ResourceUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* A022-列表容器之ExpandableListView
*
* @author devilwwj
*/
public class ExpandableListViewTestActivity extends ActionBarActivity implements ExpandableListViewAdapter.OnGroupClickListener, ExpandableListViewAdapter.OnGroupExpandListener
, ExpandableListViewAdapter.OnChildItemClickListener {
private ExpandableListView expandableListView;
private Context mContext;
private ExpandableListViewAdapter expandAdapter;
private List<GroupItem> groupList;
private List<List<Category>> childList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expandablelistview);
mContext = ExpandableListViewTestActivity.this;
expandableListView = (ExpandableListView) this.findViewById(R.id.expandablelistview);
expandableListView.setGroupIndicator(null); // 去掉默认指示器
// 设置展开列表数据
setExpandableListView();
}
private void setExpandableListView() {
try {
// 这里分别模拟组项和子项数据
groupList = new ArrayList<GroupItem>();
childList = new ArrayList<List<Category>>();
ResourceUtil resourceUtil = new ResourceUtil(this);
// 从本地获取目录
String str = getString(R.string.categories);
JSONObject jsonObject = new JSONObject(str);
JSONObject categoryObj = jsonObject.optJSONObject("categories");
// 左边侧边栏
JSONArray jsonArray = categoryObj.optJSONArray("left");
for (int i = 0; i < jsonArray.length(); i++) {
JSONArray groupArray = jsonArray.getJSONArray(i);
GroupItem groupItem = new GroupItem();
groupItem.setDrawableId(resourceUtil.getResId("ic_leftnav_"
+ (i + 1), "drawable"));
JSONObject groupObj = groupArray.optJSONObject(0);
groupItem.setGroupId(groupObj.optString("cat_id")); // 设置组别的分类id
groupItem.setText(groupObj.optString("title"));
groupList.add(groupItem);
List<Category> categories = new ArrayList<Category>();
if (groupArray.length() > 0) { // 如果多于一项分类
for (int index = 1; index < groupArray.length(); index++) {
JSONObject itemObj = groupArray.optJSONObject(index);
Category categorie = new Category();
categorie.setTitle(itemObj.optString("title"));
categorie.setCat_id(itemObj.optString("cat_id"));
categories.add(categorie);
}
}
childList.add(categories);
}
// 实例化适配器
expandAdapter = new ExpandableListViewAdapter(this, expandableListView, groupList, childList);
expandableListView.setAdapter(expandAdapter);
// 设置ExpandableListView的相关事件监听
// 子项选中、子项被点击、组项展开、组项被点击
// expandableListView.setOnItemSelectedListener(itemSelectedListener);
expandableListView.setOnChildClickListener(mOnChildClickListener);
// expandableListView.setOnGroupExpandListener(mOnGroupExpandListener);
// expandableListView.setOnGroupClickListener(mOnGroupClickListener);
expandAdapter.setOnGroupClickListener(this);
expandAdapter.setOnGroupExpandListener(this);
expandAdapter.setOnChildClickListener(this);
} catch (JSONException e) {
e.printStackTrace();
}
}
final private ExpandableListView.OnChildClickListener mOnChildClickListener = new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
final int groupPosition, final int childPosition, long id) {
// 设置子项被选中的背景
// expandAdapter.setItemChecked(groupPosition, childPosition);
// 返回true,列表不可展开
return false;
}
};
final private ExpandableListView.OnGroupExpandListener mOnGroupExpandListener = new ExpandableListView.OnGroupExpandListener() {
@Override
public void onGroupExpand(int groupPosition) {
for (int i = 0, count = expandableListView
.getExpandableListAdapter().getGroupCount(); i < count; i++) {
expandAdapter.setExpandedGroupPosition(groupPosition);
if (groupPosition != i) { // 关闭其他组
expandableListView.collapseGroup(i);
}
}
}
};
final private ExpandableListView.OnGroupClickListener mOnGroupClickListener = new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
final int groupPosition, long id) {
return true;
}
};
@Override
public void onGroupExpand(int position) {
// 组项被展开时会回调这个方法
if (expandAdapter.getExpandStateAtPosition(position)) {
// 收起
expandableListView.collapseGroup(position);
expandAdapter.setExpandStateAtPosition(position, false);
} else {
expandableListView.expandGroup(position, true);
expandAdapter.setExpandStateAtPosition(position, true);
}
}
@Override
public void onGroupClick(int position) {
// 执行选中后的操作
}
@Override
public void onChildItemClick(int groupPosition, int childPosition) {
// 子项被点击会回调这个方法
}
}
实际开发中,我们可能会遇到其他UI上的需求,原生的效果是完全不能满足我们的,这里提一点就是,熟练掌握API和解决问题能力很重要,不管UI怎么变我们都有办法去实现,可能只要我们找到对应的API设置一下或者看看有没有大神造好了轮子,终究我们还是可以找到解决方案,在Android开发当中我们经常打交道也最头痛的是UI,多实践和学习才能更好的完成工作,谢谢大家。
转载请注明:IT_xiao小巫 http://blog.csdn.net/wwj_748
欢迎关注我的公众号:wwjblog
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:android expandablelistview it-xiao小巫
原文地址:http://blog.csdn.net/wwj_748/article/details/49287645