打造史上最没有技术含量的多级(无限级)ListView

项目中需要使用一个多层级关系的ListView,问了下需求,回答是无限级。。。好吧,参考了网上的各种资源,似乎对无限级这种都没有很好的解决方案,特别是在后台给我的数据仅仅告诉我上一级的名称,大致就是{“私有属性”,”上一级菜单名称”},偶尔看到一个开发人员的一个提示,所谓的展开就是增加item,关闭就是删除item,顿时有了一个极其笨的办法。既然是最没有技术含量的,所以这里也会有一些问题,最严重的问题就是——性能低下,如果处理数量大的,还是另辟蹊径吧。
闲话少说,开始正文:
1.数据bean类 MenuData.class

    private String name; // 本级菜单的名字
    private String text; // 服务器传来要展示的数据,可以是任意数据形式
    private String top_menu; // 上级菜单名称,无论返回上下级菜单关系,原理是一样的
    private boolean isNext; // 是否有下一级菜单
    private boolean isOpen; // 菜单是否展开
    private int level; // 所在层级

//setter getter...

这边后台太弱,我这边的层级和是否有下一级菜单,都是我自己定义和计算出来的,如果后台强大,完全没必要这个步骤。

黑体加粗强调下,得到的数据先一律将isOpen设为false,因为都是关闭的,这个步骤我是在获取数据的地方设置的
2.整理服务器得来的数据

// 整理从服务器获取的数据
    private void initData(List<MenuData> list) {
        // 得到第一级菜单
        List<MenuData> menuList = new ArrayList<>();
        menuList.clear(); // 习惯问题,清一下,就跟刷新一样
        for (MenuData data : list) {
            // 我这边第一级菜单服务器返回的上级菜单是"null",对,没看错是字符串"null"
            if ("null".equals(data.getTop_menu())) {
                // 设置层级
                data.setLevel(1);
                // 寻找是否有下级菜单
                for (MenuData data1 : list) {
                    if (data.getName().equals(data1.getTop_menu())) {
                        // 存在即说明有
                        data.setNext(true);
                        break;
                    } else {
                        // 一直没有那就是没有咯
                        data.setNext(false);
                    }
                }
                // 把一级菜单都加到菜单列表中
                menuList.add(data);
            }
        }
        // 这里创建一个菜单map,key是菜单的名字,value是该菜单名字下的下一级菜单集合
        menuMap = new HashMap<>();
        menuMap.put("null", menuList);

        // 差不多就可以可以感受到没有技术含量的代码了,两次for循环完了,再来三次
        for (MenuData data : list) {
            String name = data.getName();
            // 一定要在new一次,也就是一个新的对象,而不是clear,不然。。。你试试
            menuList = new ArrayList<>();
            for (int j = 0; j < list.size(); j++) {
                if (name.equals(list.get(j).getTop_menu())) {
                    for (MenuData data1 : list) {
                        if (data.getName().equals(data1.getTop_menu())) {
                            data.setNext(true);
                            break;
                        } else {
                            data.setNext(false);
                        }
                    }
                    menuList.add(list.get(j));
                }
            }
            menuMap.put(name, menuList);
        }


        // 此处的mList是与ListViw绑定的list,也就是负责数据更新的,此处是让页面进入即显示第一级菜单
        mList.clear();
        mList.addAll(menuMap.get("null"));
        mAdapter.notifyDataSetChanged();
    }

看到没有,如果后台够强大,直接返回层级和是否有下级菜单,将省下很多事情,什么你看到我mList,mAdapter都没定义,别闹,这个你应该会啊,来给你完整代码

    mListView = (ListView) v.findViewById(R.id.list_view);
    mList = new ArrayList<>();
    mAdapter = new MenuAdapter(mContext, mList);
    mListView.setAdapter(mAdapter);

3.adapter中的处理
什么叫史上最没有技术含量,那就是,没有什么特别要求,你以前怎么写的,现在怎么写就行了,完全一样。好吧,我有一点小要求,比如缩进,比如打开和关闭的图标不同,背景颜色要不一样。给关键代码吧

// 背景颜色,因为我的是白色文字,所以这四种颜色比较搭,各位自行选择啊
private int[] colors = {0xFF000000, 0xFF0033CC, 0xFF01CC00, 0xFFFF3300};

... 此处省略各种写adapter就要重复的代码

    MenuData data = mList.get(position);
        vh.tx.setText(data.getText());
        vh.name.setText(data.getName());
        // 把图标进行缩进,因为我的图标是在最前面,自行选择哪个控件缩进
        vh.icon.setPadding((data.getLevel() - 1) * 8, 8, 8, 8);
        vh.layout.setBackgroundColor(colors[data.getLevel() % 4]);
        // 判断是否有下级菜单
        if (!data.isNext()) {
            // 没有下级菜单显示圆圈
            vh.icon.setImageResource(R.mipmap.knowledgetree_leaf);
        } else if (data.isOpen()) {
            // 有下级菜单,且为展开状态
            vh.icon.setImageResource(R.mipmap.knowledgetree_rootexpanded);
        } else {
            // 有下级菜单,且为关闭状态
            vh.icon.setImageResource(R.mipmap.knowledgetree_rootunexpanded);
        }

我比较大方,送你三张图片了
没有下级菜单
打开状态
关闭状态

还有什么事要做了?最重要的,展开和关闭状态要实现啊,不然要写这个干吗啊。。。
各位要想按图标进行展开和关闭了,就在adapter里面设置,嗯,会吧?我是整个item点击有效,所以我就是在页面的类里面写的了。
如果要在adapter里面写,给个思路吧,设置监听的时候,把position带上就ok了。
下面那个是我在这个项目中基本类似需求的代码,我就不在这说了。

class Click implements View.OnClickListener {

        int position;

        public Click(int position) {
            this.position = position;
        }

        @Override
        public void onClick(View v) {
            Data data = mList.get(position);
            switch (v.getId()) {
                case R.id.btn1:
                   break;
                case R.id.btn2:
                 break;

            }
        }
    }

4.数据更新

 private class ItemClick implements android.widget.AdapterView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            MenuData menuData = mList.get(position);
            // 因为要对数据更新,所以先把数据暂存下来
            List<MenuData> tempList = new ArrayList<>();
            tempList.addAll(mList);
            if (!menuData.isOpen() && menuData.isNext()) {
                // 记录该菜单是展开状态
                menuData.setOpen(true);
                mList.clear();
                // 又到体现最没技术含量的时刻到了,简单点说就是,把点击项的前面原样放入list,把点击的那项下一级菜单加进去,再把原来的后部分数据加进去
                for (int tempI = 0; tempI <= position; tempI++) {
                    mList.add(tempList.get(tempI));
                }
                // 获得点击的下级菜单,并插入
                for (MenuData data : menuMap.get(menuData.getName())) {
                    // 服务器没有返回层级,所以自己计算层级,很容易理解
                    data.setLevel(menuData.getLevel() + 1);
                    // 这里的作用是再怎么没有技术含量也要适当考虑性能,计算目前展开的最大层级,方便下面的关闭菜单,下面会讲到
                    if (menuData.getLevel() + 1 > maxOpen) {
                        maxOpen = menuData.getLevel() + 1;
                    }
                    mList.add(data);
                }
                for (int tempI = position + 1; tempI < tempList.size(); tempI++) {
                    mList.add(tempList.get(tempI));
                }
                // 这里就要进行关闭菜单了
            } else if (menuData.isOpen() && menuData.isNext()) {
                menuData.setOpen(false);
                String delName = menuData.getName();
                // 这里要注意,与展开不同,展开只要插入下一级菜单,而关闭是要将下一级及以下包含的各级全部移除,这里知道我上面要定义展开最大层级的作用了吧
                for (int i = 0; i < tempList.size(); i++) {
                    MenuData delData = tempList.get(i);
                    String delTopName = delData.getTop_menu();
                    for (int tempI = menuData.getLevel(); tempI < maxOpen; tempI++) {
                        if (delTopName.equals(delName)) {
                            delData.setOpen(false);
                            mList.remove(delData);
                        } else {
                            for (int j = 0; j < tempList.size(); j++) {
                                if (tempList.get(j).getName().equals(delTopName)) {
                                    delTopName = tempList.get(j).getTop_menu();
                                }
                            }
                        }
                    }
                }
            }
            mAdapter.notifyDataSetChanged();
        }
    }

因为没有技术含量,所以有点难以理解,主要是要理解核心思想:展开就是增加,关闭就是删除,以上代码就好理解了。

因为这是实际商业项目中的内容,所以我改了一些变量名字,而且不是在开发工具中,是在该博客中写的,所以可能会有误差,希望各位理解核心思想,仅供参考,copy代码极易出错。However,我在周末一定把能用的demo放上。哈哈。。。能copy还是copy吧。

遇到的问题:这只能说是一个替代方案,这里的无限级虽然可以实现,但是效率很低,其次,如果考虑每一级都有缩进的话,会发现item占满全屏了,那么显示就会很乱,这里就需要左右滑动的效果,我还没有实现,如果有解决方案的希望一起探讨。

附上demo地址
demo地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值