Androidマルチレベルツリー選択リストの高速化

9043 ワード

Androidマルチレベルツリーリストを迅速に実現し、このライブラリは鴻洋マルチレベルツリーリストdemoで修正されました.
解決された問題:
  • は、IDがintタイプおよびStringタイプであることをサポートする.
  • は、1行のコードを使用するマルチレベルチェックボックスの選択をサポートします.
  • は、データの動的更新をサポートし、既存の展開/停止状態を維持します.
  • はListView、RecyclerViewをサポートします.

  • プロジェクトのアドレス:https://github.com/zhangke3016/MultilevelTreeList
    一、概説
    この数日のプロジェクトにはマルチレベルリストのメニューがあり、最初はリストをネストするか、ExpandableListViewを使うかのような感じがしましたが、問題はExpandableListViewが2つのレベルリストしかサポートしていないことです.そして、肝心なのは具体的にいくつかのレベルに分けるのは不確定です.つまり、1つのレベルかもしれません.マルチレベルかもしれません.これは5、6つのレベルのリストをネストするなら、それは酸っぱいと思います.最終的にサボる心理状態に駆られてネット上で似たようなものがあるかどうかを調べてみると、確かに鴻洋大男の前に書いたAndroidマルチレベルの木のリストを実現する文章が巧みに実現され、1つのListViewを使ってマルチレベルのリスト効果を実現することができ、downloadの下でdemoについて、demoの基礎の上で一部の修正を行い、機能は順調に実現した.
    実はここで終わりますが、使用中に遭遇したいくつかの問題は、これをさらに最適化できると思います.例えば、私はマルチレベルのチェックリストを作り、サブレベルの選択と親レベルの選択を処理するたびに疲れて、どちらが再帰的に間違っているのか心配しています.また、新規データを更新する際にページを更新するのも自分で処理する必要があり、直接更新しても効果がないうえ、現在ではRecyclerViewが多く使われています.週末によくまとめて、使いやすいライブラリをパッケージして、次のようなニーズがあるときに使いやすいようにしたいと思っています.結局、次のためにこっそり怠けてもいいですよ.仲間に似たようなニーズがあれば、そのまま持ってきてもいいですよ~
    まず効果を見てみましょう.
    MultilevelTreeList
    この文章は主にこのライブラリの使用方法を紹介し、具体的な実装の詳細に興味があれば、ソースコードを表示したり、鴻洋のブログを検索したりすることができます.
    二、具体的な使用
    関連リストツリーには、現在のid、親id、すなわちpidの3つの必須要素が必要です.idおよびpidはintまたはStringおよび他のタイプであってもよい.表示する内容は包装する必要があります.
    //id pid name  FileNode       Bean  
    mlist.add(new Node("223","0","      root  ",new FileNode()));
    

    ListViewの場合、次のようにTreeListViewAdapterから継承する必要があります.
    public class SimpleTreeAdapter extends TreeListViewAdapter
    {
        public SimpleTreeAdapter(ListView mTree, Context context, List datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
            super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
        }
    
        public SimpleTreeAdapter(ListView mTree, Context context, List 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 datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
            super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
        }
    
        public SimpleTreeRecyclerAdapter(RecyclerView mTree, Context context, List 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:
            //       ListView
            //          
            //          
            //               0    
            //            
            //            
             mAdapter = new SimpleTreeAdapter(mTree, ListViewActivity.this,
                            mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
             mTree.setAdapter(mAdapter);
    

    RecyclerView:
            //       RecyclerView
            //          
            //          
            //               0    
            //            
            //            
            mAdapter = new SimpleTreeRecyclerAdapter(mTree, RecyclerViewActivity.this,
                    mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
    
            mTree.setAdapter(mAdapter);
    

    既存の選択または展開を維持するデータを追加します.
            List mlist = new ArrayList<>();
            mlist.add(new Node("223","0","      root  ",new FileNode()));
            mAdapter.addData(0,mlist);
    

    選択内容を取得:nodeのisChecked()がtrueの場合、選択状態です.
        StringBuilder sb = new StringBuilder();
            //      nodes
            //           mDatas  
            final List 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();
    

    親子の連動する選択解除ステータスを制御するには、setCheckedメソッドを呼び出すだけでよいが、setOnCheckedChangeListenerで処理すると問題が発生することに注意してください.なぜなら、子ノード/親ノードの選択またはキャンセルにページのリフレッシュが必要な場合、リフレッシュページはviewHolder.cb.setChecked(true/false);の判断をトリガーし、setOnCheckedChangeListenerに入り、親ノードが一部の子ノードを選択してキャンセルできない場合があるからです.
     //viewHolder.cb  CheckBox
     viewHolder.cb.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    setChecked(node,viewHolder.cb.isChecked());
                }
            });
    

    三、簡単な紹介
    すべてのデータを1つのListViewで表示し、各レベルのコンテンツの表示は、現在の表示データのレベルに応じてpaddingの値をインデントし、インデント効果があるように見えます.
    使用中に不快な感じがするのは、最終的にインタフェースエンティティBeanに表示されるのは、私たちが転送したデータではなく、変換されてフィルタされたデータです.このような最も直接的な影響は、私がデータのデータを追加した後、Adapterを持ってリフレッシュしたときに、何の効果もありません.後で追加したデータを変換していないからです.
    既存のデータ構造を変更せずに、新しいコンテンツを追加し、既存の選択を維持したり、正常に展開したりするにはどうすればいいのでしょうか.私の考えでは、変換されたノードタイプのデータを直接伝えることができればいいと思います.継承を考えて、エンティティクラスにベースクラスNodeを継承させますが、Nodeを継承すると、エンティティクラスが他のクラスを継承できないことを意味し、柔軟性がなく、エンティティクラス自体の構造にも影響します.その後、包装設計モードのいくつかのものを考えて、私は実体クラスの外でもう1層包んで、つまり実体クラスをノードに伝えて、最終的に私たちが使っているのはノードですが、node.beanで簡単に実体クラスを取り出して他の操作をすることができて、実体クラス自体の構造は破壊されていません.
    これに基づいて、ノードは変換して再作成する必要がないため、展開、選択などのステータスを保存できます.また、新しいデータを追加するときは、新しいデータをマークするだけで、新しいデータを初期化するだけで、古いデータはステータスを変更しません.
     if (node.isNewAdd && defaultExpandLeval >= currentLevel) {
                node.setExpand(true);
            }
    

    これにより、動的更新を維持してデータを追加し、既存の展開や選択状態を変化させずに、ニーズを実現することができます.
    四、ソース
    プロジェクトのアドレス:https://github.com/zhangke3016/MultilevelTreeList