ExpandableListView万能adapter封装轻松实现分组列表及单选效果

在项目开发中有时候会碰到类似于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);
        }
    }

效果如下:
这里写图片描述

源码地址:
http://pan.baidu.com/s/1nuM223v

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值