Android多級(jí)列表

需求分析

將課本的章節(jié)以多級(jí)列表的形式顯示。

解決方案思路

1.嵌套多級(jí) RecyclerView,兩級(jí)列表還好說,每多一級(jí)都是一場(chǎng)噩夢(mèng)。
2.ExpandableListView?但是ExpandableListView只支持兩級(jí)沦童,不滿足需求仑濒。
3.一個(gè)RecyclerView或者 ListView 來實(shí)現(xiàn),每一級(jí)節(jié)點(diǎn)的要素有:當(dāng)前節(jié)點(diǎn)id偷遗、父級(jí)節(jié)點(diǎn)id即pid墩瞳,顯示的內(nèi)容。

實(shí)現(xiàn)方案

//id pid name  FileNode為實(shí)際用的實(shí)體Bean對(duì)象
mlist.add(new Node("223","0","我也是添加的root節(jié)點(diǎn)",new FileNode()));

ListView需要繼承TreeListViewAdapter

public class SimpleTreeAdapter extends TreeListViewAdapter
{
    public SimpleTreeAdapter(ListView mTree, Context context, List<Node> datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
        super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
    }

    public SimpleTreeAdapter(ListView mTree, Context context, List<Node> datas,
                             int defaultExpandLevel) {
        super(mTree, context, datas, defaultExpandLevel);
    }

    @Override
    public View getConvertView(final Node node , int position, View convertView, ViewGroup parent)
    {

       final ViewHolder viewHolder ;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.cb = (CheckBox) convertView
                    .findViewById(R.id.cb_select_tree);
            viewHolder.label = (TextView) convertView
                    .findViewById(R.id.id_treenode_label);
            viewHolder.icon = (ImageView) convertView.findViewById(R.id.icon);
            convertView.setTag(viewHolder);

        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setChecked(node,viewHolder.cb.isChecked());
            }
        });

        if (node.isChecked()){
            viewHolder.cb.setChecked(true);
        }else {
            viewHolder.cb.setChecked(false);
        }

        if (node.getIcon() == -1) {
            viewHolder.icon.setVisibility(View.INVISIBLE);
        } else {
            viewHolder.icon.setVisibility(View.VISIBLE);
            viewHolder.icon.setImageResource(node.getIcon());
        }

        viewHolder.label.setText(node.getName());

        return convertView;
    }

    private final class ViewHolder
    {
        ImageView icon;
        CheckBox cb;
        TextView label;
    }
}

RecyclerView需繼承TreeRecyclerAdapter

public class SimpleTreeRecyclerAdapter extends TreeRecyclerAdapter {

    public SimpleTreeRecyclerAdapter(RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
        super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
    }

    public SimpleTreeRecyclerAdapter(RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel) {
        super(mTree, context, datas, defaultExpandLevel);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyHoder(View.inflate(mContext, R.layout.list_item,null));
    }

    @Override
    public void onBindViewHolder(final Node node, RecyclerView.ViewHolder holder, int position) {

        final MyHoder viewHolder = (MyHoder) holder;
        //todo do something
        viewHolder.cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setChecked(node,viewHolder.cb.isChecked());
            }
        });

        if (node.isChecked()){
            viewHolder.cb.setChecked(true);
        }else {
            viewHolder.cb.setChecked(false);
        }

        if (node.getIcon() == -1) {
            viewHolder.icon.setVisibility(View.INVISIBLE);
        } else {
            viewHolder.icon.setVisibility(View.VISIBLE);
            viewHolder.icon.setImageResource(node.getIcon());
        }

        viewHolder.label.setText(node.getName());


    }

    class MyHoder extends RecyclerView.ViewHolder{

        public CheckBox cb;

        public TextView label;

        public ImageView icon;
        public MyHoder(View itemView) {
            super(itemView);

            cb = (CheckBox) itemView
                    .findViewById(R.id.cb_select_tree);
            label = (TextView) itemView
                    .findViewById(R.id.id_treenode_label);
            icon = (ImageView) itemView.findViewById(R.id.icon);

        }
    }
}

初始化 ListView

        //第一個(gè)參數(shù)  ListView
        //第二個(gè)參數(shù)  上下文
        //第三個(gè)參數(shù)  數(shù)據(jù)集
        //第四個(gè)參數(shù)  默認(rèn)展開層級(jí)數(shù) 0為不展開
        //第五個(gè)參數(shù)  展開的圖標(biāo)
        //第六個(gè)參數(shù)  閉合的圖標(biāo)
         mAdapter = new SimpleTreeAdapter(mTree, ListViewActivity.this,
                        mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
         mTree.setAdapter(mAdapter);

初始化RecyclerView

//第一個(gè)參數(shù)  RecyclerView
        //第二個(gè)參數(shù)  上下文
        //第三個(gè)參數(shù)  數(shù)據(jù)集
        //第四個(gè)參數(shù)  默認(rèn)展開層級(jí)數(shù) 0為不展開
        //第五個(gè)參數(shù)  展開的圖標(biāo)
        //第六個(gè)參數(shù)  閉合的圖標(biāo)
        mAdapter = new SimpleTreeRecyclerAdapter(mTree, RecyclerViewActivity.this,
                mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);

        mTree.setAdapter(mAdapter);

添加數(shù)據(jù)鹦肿,可以保持原有選中或者展開狀態(tài)

List<Node> mlist = new ArrayList<>();
        mlist.add(new Node("223","0","我也是添加的root節(jié)點(diǎn)",new FileNode()));
        mAdapter.addData(0,mlist);

獲取選中內(nèi)容:如果node的isChecked()為true矗烛,即為選中狀態(tài)。

    StringBuilder sb = new StringBuilder();
        //獲取排序過的nodes
        //如果不需要刻意直接用 mDatas既可
        final List<Node> allNodes = mAdapter.getAllNodes();
        for (int i = 0; i < allNodes.size(); i++) {
            if (allNodes.get(i).isChecked()){
                sb.append(allNodes.get(i).getName()+",");
            }
        }
        String strNodesName = sb.toString();
        if (!TextUtils.isEmpty(strNodesName))
            Toast.makeText(this, strNodesName.substring(0, strNodesName.length()-1),Toast.LENGTH_SHORT).show();

控制父子之間聯(lián)動(dòng)的選中與取消狀態(tài)箩溃,只需調(diào)用setChecked方法既可,注意如果在setOnCheckedChangeListener中處理會(huì)有問題:因?yàn)槿绻庸?jié)點(diǎn)/父節(jié)點(diǎn)選中或者取消需要刷新頁面碌嘀,而刷新頁面又會(huì)觸發(fā)viewHolder.cb.setChecked(true/false);的判斷從而又會(huì)進(jìn)入setOnCheckedChangeListener涣旨,會(huì)導(dǎo)致如果父節(jié)點(diǎn)選中某些子節(jié)點(diǎn)取消不了的情況。

 //viewHolder.cb 為CheckBox
 viewHolder.cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setChecked(node,viewHolder.cb.isChecked());
            }
        });

簡(jiǎn)單介紹

通過一個(gè)ListView來展示所有數(shù)據(jù)股冗,每一級(jí)內(nèi)容的顯示根據(jù)當(dāng)前展示數(shù)據(jù)的等級(jí)縮進(jìn)一定的padding值霹陡,讓我們看起來有縮進(jìn)效果。

使用過程中感覺不是很舒服的地方在于最終用于顯示在界面實(shí)體Bean并不是我們傳進(jìn)去的數(shù)據(jù)止状,而是經(jīng)過轉(zhuǎn)化并且過濾的數(shù)據(jù)烹棉,這樣最直接的影響就是在我新增數(shù)據(jù)的數(shù)據(jù)之后,拿著Adapter來刷新的時(shí)候怯疤,并沒有任何效果浆洗。因?yàn)槲覀儧]有將后面新加的數(shù)據(jù)進(jìn)行轉(zhuǎn)化。

而我們?nèi)绾文茉诓桓淖冊(cè)袛?shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上集峦,添加我們的新內(nèi)容伏社,并保持原有的選中或者展開正常呢?我的想法是這樣的塔淤,如果可以直接給它傳入轉(zhuǎn)化后的Node節(jié)點(diǎn)類型數(shù)據(jù)就好了摘昌,我想到了繼承,讓實(shí)體類去繼承基類Node高蜂,但一旦繼承Node則意味著實(shí)體類就不能再繼承其他類了聪黎,感覺不是很靈活,而且也影響了實(shí)體類本身的結(jié)構(gòu)备恤。后來想到了包裝設(shè)計(jì)模式的一些東西稿饰,那我就在實(shí)體類外再包上一層,也就是將實(shí)體類傳給Node烘跺,最終我們使用的還是Node湘纵,但也可以用node.bean很輕松的取出實(shí)體類做其他操作,并且實(shí)體類本身的結(jié)構(gòu)并沒有被破壞滤淳。

在此基礎(chǔ)上梧喷,因?yàn)槲覀兊腘ode不需要轉(zhuǎn)化重新創(chuàng)建,那么它就可以保存一些狀態(tài)比如展開、選中等等铺敌,而在新加入數(shù)據(jù)時(shí)只需標(biāo)記下新加入的數(shù)據(jù)汇歹,只需對(duì)新加入的數(shù)據(jù)進(jìn)行初始化狀態(tài),已有老數(shù)據(jù)不進(jìn)行狀態(tài)改變:

 if (node.isNewAdd && defaultExpandLeval >= currentLevel) {
            node.setExpand(true);
        }

項(xiàng)目地址:https://github.com/zhangke3016/MultilevelTreeList
原文連接:http://www.reibang.com/p/090904d2b689

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末偿凭,一起剝皮案震驚了整個(gè)濱河市产弹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弯囊,老刑警劉巖痰哨,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異匾嘱,居然都是意外死亡斤斧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門霎烙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撬讽,“玉大人,你說我怎么就攤上這事悬垃∮沃纾” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵尝蠕,是天一觀的道長(zhǎng)烘豌。 經(jīng)常有香客問我,道長(zhǎng)趟佃,這世上最難降的妖魔是什么扇谣? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮闲昭,結(jié)果婚禮上罐寨,老公的妹妹穿的比我還像新娘。我一直安慰自己序矩,他們只是感情好鸯绿,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著簸淀,像睡著了一般瓶蝴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上租幕,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天舷手,我揣著相機(jī)與錄音,去河邊找鬼劲绪。 笑死男窟,一個(gè)胖子當(dāng)著我的面吹牛盆赤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歉眷,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼牺六,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了汗捡?” 一聲冷哼從身側(cè)響起淑际,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扇住,沒想到半個(gè)月后春缕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艘蹋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年淡溯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片簿训。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖米间,靈堂內(nèi)的尸體忽然破棺而出强品,到底是詐尸還是另有隱情,我是刑警寧澤屈糊,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布的榛,位于F島的核電站,受9級(jí)特大地震影響逻锐,放射性物質(zhì)發(fā)生泄漏夫晌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一昧诱、第九天 我趴在偏房一處隱蔽的房頂上張望晓淀。 院中可真熱鬧,春花似錦盏档、人聲如沸凶掰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懦窘。三九已至,卻和暖如春稚配,著一層夾襖步出監(jiān)牢的瞬間畅涂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工道川, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留午衰,地道東北人立宜。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像苇经,于是被迫代替她去往敵國(guó)和親赘理。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容