程序员人生 网站导航

A022-列表容器之ExpandableListView

栏目:综合技术时间:2016-02-28 11:12:28

概述

本节课介绍Android中可实现2级可展开收缩列表的ExpandableListView容器,笔者感觉它非常难用并且难理解,很多时候我们可能需要对控件进行扩大和定制,但是它不太方便扩大,它使用难点主要在数据结构上和对控件的事件监听,其他的实现方式类似ListView,下面会提供笔者在实际开发中使用到的案例。

案例

ExpandableListView

上面实现的效果可展开的2级列表,每一个组项都可能有若干个子项,默许的ExpandableListView不太美观,我们需要通过自定义布局类美化它,在使用进程中有1些需要我们去了解的点,会在实现进程提1下。

实现进程

源码已上传到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

<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

<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

<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" > <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)
- 传入子项列表(如:List)

适配器代码:
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 ListgroupList; // 组列表 private ListchildList; // 子项列表 private LayoutInflater inflater; // 自定义接口回调监听器 private OnGroupExpandListener OnGroupExpandListener; private OnGroupClickListener onGroupClickListener; private OnChildItemClickListener onChildClickListener; private HashMap maps = new HashMap(); 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, ListgroupList, ListchildList) { 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); } }

解析1下上面的代码,我们可以看到ExpandableListView除1个组项,每一个组项下面有若干个子项,我们在使用的时候首先要肯定要展现的数据结构,组项有groupPosition来标识位置,但是子项需要根据groupPosition和ChildPosition来标识位置,我们设置数据的时候分别在getGroupView和getChildView方法来设置组视图和子项视图数据,最后返回填充数据的视图对象,1些逻辑控制的代码也是在这两个方法中进行,比如控制组项的展开、组项的点击、子项的点击、子项被选中效果等等,这里笔者是自定义了回调接口来满足业务的需求,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 ListgroupList; private ListchildList; @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(); childList = new ArrayList(); 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); Listcategories = new ArrayList(); if (groupArray.length() > 0) { // 如果多于1项分类 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上的需求,原生的效果是完全不能满足我们的,这里提1点就是,熟练掌握API和解决问题能力很重要,不管UI怎样变我们都有办法去实现,可能只要我们找到对应的API设置1下或看看有无大神造好了轮子,终究我们还是可以找到解决方案,在Android开发当中我们常常打交道也最头痛的是UI,多实践和学习才能更好的完成工作,谢谢大家。

转载请注明:IT_xiao小巫 http://blog.csdn.net/wwj_748

欢迎关注我的公众号:wwjblog
wwjblog

版权声明:本文为博主原创文章,未经博主允许不得转载。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐