Android多級列表

需求分析

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

解決方案思路

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

實現(xiàn)方案

//id pid name  FileNode為實際用的實體Bean對象
mlist.add(new Node("223","0","我也是添加的root節(jié)點",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

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

初始化RecyclerView

//第一個參數(shù)  RecyclerView
        //第二個參數(shù)  上下文
        //第三個參數(shù)  數(shù)據(jù)集
        //第四個參數(shù)  默認(rèn)展開層級數(shù) 0為不展開
        //第五個參數(shù)  展開的圖標(biāo)
        //第六個參數(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é)點",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)動的選中與取消狀態(tài),只需調(diào)用setChecked方法既可,注意如果在setOnCheckedChangeListener中處理會有問題:因為如果要子節(jié)點/父節(jié)點選中或者取消需要刷新頁面,而刷新頁面又會觸發(fā)viewHolder.cb.setChecked(true/false);的判斷從而又會進入setOnCheckedChangeListener,會導(dǎo)致如果父節(jié)點選中某些子節(jié)點取消不了的情況。

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

簡單介紹

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

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

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

在此基礎(chǔ)上,因為我們的Node不需要轉(zhuǎn)化重新創(chuàng)建,那么它就可以保存一些狀態(tài)比如展開、選中等等,而在新加入數(shù)據(jù)時只需標(biāo)記下新加入的數(shù)據(jù),只需對新加入的數(shù)據(jù)進行初始化狀態(tài),已有老數(shù)據(jù)不進行狀態(tài)改變:

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

項目地址:https://github.com/zhangke3016/MultilevelTreeList
原文連接:http://www.itdecent.cn/p/090904d2b689

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容