在项目开发中有时候会碰到类似于QQ好友列表的分组列表效果的需求,实现的方式有很多种,可以用ListView、RecyclerView等方式实现,其实系统提供了ExpandableListView可以很轻松的实现;ExpandableListView extends ListView所有在用法上和ListView差不多,都需要设置adapter,但是ExpandableListView的adapter需要extends BaseExpandableListAdapter,需要重写里面的getGroupCount()、getChildrenCount()、getGroup()等方法,每写一个adapter就要去重写这些方法,还是挺麻烦的,同时也造成代码的冗余,所有在这里对ExpandableListView adapter进行了封装。
父条目ViewHolder:
public abstract class ParentHolder<T> {
private View convertView;
public ParentHolder() {
convertView = initView();
convertView.setTag(this);
}
public View getConvertView() {
return convertView;
}
@SuppressWarnings("unchecked")
protected <T extends View> T findID(View v, int id) {
return (T) v.findViewById(id);
}
public abstract void refreshView(List<T> list, int position, boolean isExpanded);//初始化页面数据
public abstract View initView();//加载页面ui
}
子条目ViewHolder:
public abstract class ChildHolder<T> {
private View convertView;
public ChildHolder() {
convertView = initView();
convertView.setTag(this);
}
public View getConvertView() {
return convertView;
}
@SuppressWarnings("unchecked")
protected <T extends View> T findID(View v, int id) {
return (T) v.findViewById(id);
}
public abstract void refreshView(List<T> list, int position);//初始化页面数据
public abstract View initView();//加载页面ui
}
adapter:
public abstract class DefaultAdapter<T> extends BaseExpandableListAdapter {
private Context mContext;
private List<T> parentLists;
private Map<T, List<T>> mMap;
public DefaultAdapter(Context context, List<T> parentList, Map<T, List<T>> map) {
this.mContext = context;
this.parentLists = new ArrayList<>();
if (parentLists != null) {
this.parentLists.addAll(parentList);
}
this.mMap = new HashMap<>();
if (mMap != null) {
this.mMap.putAll(map);
}
}
/**
* 刷新Group数据
*
* @param list
*/
public void nodfiyParentData(List<T> list) {
if (list != null) {
this.parentLists.clear();
this.parentLists.addAll(list);
}
notifyDataSetChanged();
}
/**
* 刷新map数据
*
* @param map
*/
public void nodfiyMapData(Map<T, List<T>> map) {
if (map != null) {
this.mMap.clear();
this.mMap.putAll(map);
}
notifyDataSetChanged();
}
/**
* 父条目的数量
*
* @return
*/
@Override
public int getGroupCount() {
return parentLists.size();
}
/**
* 每个父条目对应的子条目的数量
*
* @param groupPosition
* @return
*/
@Override
public int getChildrenCount(int groupPosition) {
T t = parentLists.get(groupPosition);
List<T> ts = mMap.get(t);
if (ts == null) {
ts = new ArrayList<>();
}
return ts.size();
}
/**
* 根据父条目的位置获取对应的对象
*
* @param groupPosition
* @return
*/
@Override
public Object getGroup(int groupPosition) {
return parentLists.get(groupPosition);
}
/**
* 根据子条目的位置获取对应的对象
*
* @param groupPosition
* @param childPosition
* @return
*/
@Override
public Object getChild(int groupPosition, int childPosition) {
T t = parentLists.get(groupPosition);
List<T> ts = mMap.get(t);
if (ts == null) {
ts = new ArrayList<>();
}
return ts.get(childPosition);
}
/**
* 父位置
*
* @param groupPosition
* @return
*/
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
/**
* 子位置
*
* @param groupPosition
* @param childPosition
* @return
*/
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
/**
* 父布局
*
* @param groupPosition
* @param isExpanded
* @param convertView
* @param parent
* @return
*/
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
ParentHolder<T> holder = null;
if (convertView == null) {
holder = getParentHolder();
} else {
holder = (ParentHolder<T>) convertView.getTag();
}
//isExpanded group是否有展开
if (groupPosition < parentLists.size()) {
holder.refreshView(parentLists, groupPosition, isExpanded);
}
return holder.getConvertView();
}
protected abstract ParentHolder<T> getParentHolder();
/**
* 子布局
*
* @param groupPosition
* @param childPosition
* @param isLastChild
* @param convertView
* @param parent
* @return
*/
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildHolder<T> holder = null;
if (convertView == null) {
holder = getChildHolder();
} else {
holder = (ChildHolder<T>) convertView.getTag();
}
T t = parentLists.get(groupPosition);
List<T> ts = mMap.get(t);
holder.refreshView(ts, childPosition);
return holder.getConvertView();
}
protected abstract ChildHolder<T> getChildHolder();
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
//返回true才会触发setOnChildClickListener子条目事件
return true;
}
}
以上就是对ExpandableListView adapter的封装,在使用的时候去extends DefaultAdapter 去实现布局view的加载和数据填充就可以了,这样就方便了很多;
public class ExpandableActivity extends AppCompatActivity {
private ExpandableListView expandedList;
private List<DataInfo> pList = new ArrayList<>();
private Map<DataInfo, List<DataInfo>> cMap = new HashMap<>();
private ExpandableAdapter adapter;
private int groupItemSelect = -1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expandable);
setData();
expandedList = (ExpandableListView) findViewById(R.id.expanded_list);
adapter = new ExpandableAdapter(this, pList, cMap);
//设置adapter
expandedList.setAdapter(adapter);
//子条目点击事件
expandedList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
if (groupItemSelect == -1 || groupItemSelect == groupPosition) {
setChildSelect(groupPosition, childPosition);
} else {
setChildSelect(groupPosition, childPosition);
DataInfo dataInfo2 = pList.get(groupItemSelect);
List<DataInfo> dataInfos1 = cMap.get(dataInfo2);
for (DataInfo info : dataInfos1) {
info.childItemSelect = false;
}
cMap.put(dataInfo2, dataInfos1);
}
Toast.makeText(ExpandableActivity.this, "groupPosition-->" + groupPosition + "childPosition-->" + childPosition, Toast.LENGTH_LONG).show();
adapter.nodfiyMapData(cMap);
groupItemSelect = groupPosition;
return false;
}
});
//Group点击事件
expandedList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
DataInfo dataInfo = pList.get(groupPosition);
Toast.makeText(ExpandableActivity.this, dataInfo.itemTitle, Toast.LENGTH_LONG).show();
return false;
}
});
//设置group左边图标 设置为null代表将其隐藏掉
expandedList.setGroupIndicator(null);
spreadListView();
}
private void setChildSelect(int group, int child) {
DataInfo dataInfo = pList.get(group);
List<DataInfo> dataInfos = cMap.get(dataInfo);
DataInfo dataInfo1 = dataInfos.get(child);
String childItemId = dataInfo1.childItemId;
for (DataInfo info : dataInfos) {
String itemId = info.childItemId;
if (childItemId.equals(itemId)) {
info.childItemSelect = true;
} else {
info.childItemSelect = false;
}
}
cMap.put(dataInfo, dataInfos);
}
/**
* 默认展开第一组 要在setAdapter后调用
*/
private void spreadListView() {
int groupCount = adapter.getGroupCount();
if (groupCount > 0) {
expandedList.expandGroup(0);
}
}
/**
* 数据适配器
*/
class ExpandableAdapter extends DefaultAdapter<DataInfo> {
public ExpandableAdapter(Context context, List<DataInfo> parentList, Map<DataInfo, List<DataInfo>> map) {
super(context, parentList, map);
}
@Override
protected ParentHolder<DataInfo> getParentHolder() {
return new PHolder();
}
@Override
protected ChildHolder<DataInfo> getChildHolder() {
return new CHolder();
}
}
/**
* group holder
*/
class PHolder extends ParentHolder<DataInfo> {
TextView tvGroup;
ImageView ivIcon;
@Override
public void refreshView(List<DataInfo> list, int position, boolean isExpanded) {
DataInfo dataInfo = list.get(position);
tvGroup.setText(dataInfo.itemTitle);
//判断group是否有展开
if (isExpanded) {
ivIcon.setImageResource(R.drawable.up);
} else {
ivIcon.setImageResource(R.drawable.down);
}
}
@Override
public View initView() {
View view = LayoutInflater.from(ExpandableActivity.this).inflate(R.layout.expandable_group, null, false);
tvGroup = findID(view, R.id.tv_group);
ivIcon = findID(view, R.id.iv_icon);
return view;
}
}
/**
* child holder
*/
class CHolder extends ChildHolder<DataInfo> {
TextView tvChild;
ImageView ivCheck;
@Override
public void refreshView(List<DataInfo> list, int position) {
DataInfo dataInfo = list.get(position);
tvChild.setText(dataInfo.itemTitle);
boolean childItemSelect = dataInfo.childItemSelect;
if (childItemSelect) {
ivCheck.setImageResource(R.drawable.check);
} else {
ivCheck.setImageResource(R.drawable.uncheck);
}
}
@Override
public View initView() {
View view = LayoutInflater.from(ExpandableActivity.this).inflate(R.layout.expandable_child, null, false);
tvChild = findID(view, R.id.tv_child);
ivCheck = findID(view, R.id.iv_check);
return view;
}
}
private void setData() {
for (int i = 0; i < 5; i++) {
DataInfo info = new DataInfo();
info.itemTitle = "group" + i;
pList.add(info);
}
for (DataInfo dataInfo : pList) {
List<DataInfo> chList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
DataInfo info = new DataInfo();
info.itemTitle = "child" + i;
info.childItemId = dataInfo.itemTitle + "child" + i;
info.childItemSelect = false;
chList.add(info);
}
cMap.put(dataInfo, chList);
}
}
效果如下: