1. 程式人生 > >打造史上最沒有技術含量的多級(無限級)ListView

打造史上最沒有技術含量的多級(無限級)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佔滿全屏了,那麼顯示就會很亂,這裡就需要左右滑動的效果,我還沒有實現,如果有解決方案的希望一起探討。