android多级树形结构列表(完整代码)

版权声明: https://blog.csdn.net/xu_coding/article/details/81490297

多级列表是个很常见的功能,发现很多代码都不好扩展,或者由于数据结构设计不好,导致开发维护比较费时间。下面自己写了一个,github连接地址: github地址分享一波。如图所示
列表图片
看一下节点的代码,最重要的设计TreePoint

public class TreePoint {
    private String ID;        // 7241,          //账号id
    private String NNAME;     //名称
    private String PARENTID;   // 0,           //父id     0表示根节点
    private String ISLEAF;     //0,            //是否是叶子节点   1为叶子节点
    private int DISPLAY_ORDER; // 1       //同一个级别的显示顺序
    private boolean isExpand = false;  //是否展开了
}

先看下MainActivity

public class MainActivity extends AppCompatActivity {

    private ReasonAdapter adapter;
    private ListView listView;
    private EditText et_filter;
    private List<TreePoint> reasonPointList = new ArrayList<>();
    private HashMap<String, TreePoint> reasonMap = new HashMap<>();


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView();
        init();
        addListener();
    }


    public void setContentView() {
        setContentView(R.layout.activity_main);
    }


    public void init() {
        adapter = new ReasonAdapter(this, reasonPointList, reasonMap);
        listView =  findViewById(R.id.listView);
        listView.setAdapter(adapter);
        et_filter =  findViewById(R.id.et_filter);
        initData();
    }
    //初始化数据
    //数据特点:TreePoint 之间的关系特点   id是任意唯一的。    如果为根节点 PARENTID  为"0"   如果没有子节点,也就是本身是叶子节点的时候ISLEAF = "1"
    //  DISPLAY_ORDER 是同一级中 显示的顺序
    //如果需要做选中 单选或者多选,只需要给TreePoint增加一个选中的属性,在ReasonAdapter中处理就好了
    private void initData() {
        reasonPointList.clear();
        int id =1000;
        int parentId = 0;
        int parentId2 = 0;
        int parentId3 = 0;
        for(int i=1;i<5;i++){
            id++;
            reasonPointList.add(new TreePoint(""+id,"分类"+i,"" + parentId,"0",i));
            for(int j=1;j<5;j++){
                if(j==1){
                    parentId2 = id;
                }
                id++;
                reasonPointList.add(new TreePoint(""+id,"分类"+i+"_"+j,""+parentId2,"0",j));
                for(int k=1;k<5;k++){
                    if(k==1){
                         parentId3 = id;
                    }
                    id++;
                    reasonPointList.add(new TreePoint(""+id,"分类"+i+"_"+j+"_"+k,""+parentId3,"1",k));
                }
            }
        }
        //这里打乱数据的顺序,模拟数据乱序,重新排序
        Collections.shuffle(reasonPointList);
        updateData();

    }


    public void addListener() {

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                adapter.onItemClick(position);
            }
        });

        et_filter.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                searchAdapter(s);
            }
        });
    }

    private void searchAdapter(Editable s) {
        adapter.setKeyword(s.toString());
    }

    //对数据排序 深度优先
    private void updateData() {
    //这里比较重要的是 reasonPointList 这个集合中的内容又使用 reasonMap这个HashMap保存起来,主要是为了利用ArrayList遍历快,HashMap查找快的特征。提高代码的执行效率。
        for (TreePoint reasonTreePoint : reasonPointList) {
            reasonMap.put(reasonTreePoint.getID(), reasonTreePoint);
        }
        Collections.sort(reasonPointList, new Comparator<TreePoint>() {
            @Override
            public int compare(TreePoint lhs, TreePoint rhs) {
                int llevel = TreeUtils.getLevel(lhs, reasonMap);
                int rlevel = TreeUtils.getLevel(rhs, reasonMap);
                if (llevel == rlevel) {
                    if (lhs.getPARENTID().equals(rhs.getPARENTID())) {  //左边小
                        return lhs.getDISPLAY_ORDER() > rhs.getDISPLAY_ORDER() ? 1 : -1;
                    } else {  //如果父辈id不相等
                        //同一级别,不同父辈
                        TreePoint lreasonTreePoint = TreeUtils.getTreePoint(lhs.getPARENTID(), reasonMap);
                        TreePoint rreasonTreePoint = TreeUtils.getTreePoint(rhs.getPARENTID(), reasonMap);
                        return compare(lreasonTreePoint, rreasonTreePoint);  //父辈
                    }
                } else {  //不同级别
                    if (llevel > rlevel) {   //左边级别大       左边小
                        if (lhs.getPARENTID().equals(rhs.getID())) {
                            return 1;
                        } else {
                            TreePoint lreasonTreePoint = TreeUtils.getTreePoint(lhs.getPARENTID(), reasonMap);
                            return compare(lreasonTreePoint, rhs);
                        }
                    } else {   //右边级别大   右边小
                        if (rhs.getPARENTID().equals(lhs.getID())) {
                            return -1;
                        }
                        TreePoint rreasonTreePoint = TreeUtils.getTreePoint(rhs.getPARENTID(), reasonMap);
                        return compare(lhs, rreasonTreePoint);
                    }
                }
            }
        });
        adapter.notifyDataSetChanged();
    }
}

适配器 中通过缩进控制不同的级别

public class ReasonAdapter extends BaseAdapter {
    private Context mcontext;
    private Activity mActivity;
    private List<TreePoint> reasonPointList;
    private String keyword = "";
    private HashMap<String, TreePoint> reasonMap = new HashMap<>();


    public ReasonAdapter(final Context mcontext, List<TreePoint> reasonPointList, HashMap<String, TreePoint> reasonMap) {
        this.mcontext = mcontext;
        this.mActivity = (Activity) mcontext;
        this.reasonPointList = reasonPointList;
        this.reasonMap = reasonMap;
    }


    public void setKeyword(String keyword) {
        this.keyword = keyword;
        //全部闭合,然后展开
        for (TreePoint reasonTreePoint : reasonPointList) {
            reasonTreePoint.setExpand(false);
        }
        if (!"".equals(keyword)) {
            for (TreePoint reasonTreePoint : reasonPointList) {
                if (reasonTreePoint.getNNAME().contains(keyword)) {
                    //含有keyword
                    if ("0".equals(reasonTreePoint.getISLEAF())) {
                        reasonTreePoint.setExpand(true);
                    }
                    //打开所有的父级元素
                    openExpand(reasonTreePoint);
                }
            }
        }
        this.notifyDataSetChanged();

    }

    private void openExpand(TreePoint reasonTreePoint) {
        if ("0".equals(reasonTreePoint.getPARENTID())) {
            reasonTreePoint.setExpand(true);
        } else {
            reasonMap.get(reasonTreePoint.getPARENTID()).setExpand(true);
            openExpand(reasonMap.get(reasonTreePoint.getPARENTID()));
        }
    }


    //第一要准确计算数量
    @Override
    public int getCount() {
        int count = 0;
        for (TreePoint tempPoint : reasonPointList) {
            if ("0".equals(tempPoint.getPARENTID())) {
                count++;
            } else {
                if (getItemIsExpand(tempPoint.getPARENTID())) {
                    count++;
                }
            }
        }
        return count;
    }

    private boolean getItemIsExpand(String ID) {
        for (TreePoint tempPoint : reasonPointList) {
            if (ID.equals(tempPoint.getID())) {
                if (tempPoint.isExpand()) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    @Override
    public Object getItem(int position) {
        return reasonPointList.get(convertPostion(position));
    }

    private int convertPostion(int position) {
        int count = 0;
        for (int i = 0; i < reasonPointList.size(); i++) {
            TreePoint treePoint = reasonPointList.get(i);
            if ("0".equals(treePoint.getPARENTID())) {
                count++;
            } else {
                if (getItemIsExpand(treePoint.getPARENTID())) {
                    count++;
                }
            }
            if (position == (count - 1)) {
                return i;
            }
        }
        return 0;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(mcontext).inflate(R.layout.adapter_treeview, null);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.text);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            holder.contactitemBtn = (ImageButton) convertView.findViewById(R.id.contactitem_sendmsg);
            holder.contactitemBtn.setVisibility(View.GONE);
            convertView.setTag(holder);
            convertView.setBackgroundColor(0xffffffff);
            convertView.setPadding(DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10));
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        TreePoint tempPoint = (TreePoint) getItem(position);

        int level = TreeUtils.getLevel(tempPoint, reasonMap);
        holder.icon.setPadding(25 * level, holder.icon.getPaddingTop(), 0, holder.icon.getPaddingBottom());
        if ("0".equals(tempPoint.getISLEAF())) {
            if (tempPoint.isExpand() == false) {
                holder.icon.setVisibility(View.VISIBLE);
                holder.icon.setImageResource(R.drawable.outline_list_collapse);
            } else {
                holder.icon.setVisibility(View.VISIBLE);
                holder.icon.setImageResource(R.drawable.outline_list_expand);
            }
        } else {
            holder.icon.setVisibility(View.INVISIBLE);
            holder.icon.setImageResource(R.drawable.outline_list_collapse);
        }

        if (keyword != null && !"".equals(keyword) && tempPoint.getNNAME().contains(keyword)) {
            int index = tempPoint.getNNAME().indexOf(keyword);
            int len = keyword.length();
            Spanned temp = Html.fromHtml(tempPoint.getNNAME().substring(0, index)
                    + "<font color=#FF0000>"
                    + tempPoint.getNNAME().substring(index, index + len) + "</font>"
                    + tempPoint.getNNAME().substring(index + len, tempPoint.getNNAME().length()));

            holder.text.setText(temp);
        } else {
            holder.text.setText(tempPoint.getNNAME());
        }
        holder.text.setCompoundDrawablePadding(DensityUtil.dip2px(mcontext, 10));

        holder.contactitemBtn.setSelected(false);
        if (tempPoint.isExpand())
            holder.contactitemBtn.setSelected(true);
        return convertView;
    }

    public void onItemClick(int position) {
        TreePoint reasonTreePoint = (TreePoint) getItem(position);
        if ("1".equals(reasonTreePoint.getISLEAF())) {
            //处理回填
            Toast.makeText(mcontext, getSubmitResult(reasonTreePoint), Toast.LENGTH_SHORT).show();
        } else {  //如果点击的是父类
            if (reasonTreePoint.isExpand()) {
                for (TreePoint tempPoint : reasonPointList) {
                    if (tempPoint.getPARENTID().equals(reasonTreePoint.getID())) {
                        if ("0".equals(reasonTreePoint.getISLEAF())) {
                            tempPoint.setExpand(false);
                        }
                    }
                }
                reasonTreePoint.setExpand(false);
            } else {
                reasonTreePoint.setExpand(true);
            }
        }
        this.notifyDataSetChanged();
    }


    private String getSubmitResult(TreePoint reasonTreePoint) {
        StringBuilder sb = new StringBuilder();
        addResult(reasonTreePoint, sb);
        String result = sb.toString();
        if (result.endsWith("-")) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }

    private void addResult(TreePoint reasonTreePoint, StringBuilder sb) {
        if (reasonTreePoint != null && sb != null) {
            sb.insert(0, reasonTreePoint.getNNAME() + "-");
            if (!"0".equals(reasonTreePoint.getPARENTID())) {
                addResult(reasonMap.get(reasonTreePoint.getPARENTID()), sb);
            }
        }
    }


    class ViewHolder {
        TextView text;
        ImageView icon;
        ImageButton contactitemBtn;   //这是个选择的按钮,可以自己适当修改代码实现功能
    }
}
工具类  获取层级,其实这里可以将TreePoint增加一个层级属性,如果层级属性为空,就重新计算,如果不为空,就直接使用,避免滚动listView的时候多次计算。
public class TreeUtils {
    //第一级别为0
    public static int getLevel(TreePoint treePoint,HashMap<String,TreePoint> map){
        if("0".equals(treePoint.getPARENTID())){
            return 0;
        }else{
            return 1+getLevel(getTreePoint(treePoint.getPARENTID(),map),map);
        }
    }

    public static TreePoint getTreePoint(String ID, HashMap<String,TreePoint> map){
        if(map.containsKey(ID)){
            return map.get(ID);
        }
        Log.e("xlc","ID:" + ID);
        return null;
    }
}

以上大致就是所有的代码了。主要是为了实现多级列表。方便扩展。要保证效率。

猜你喜欢

转载自blog.csdn.net/xu_coding/article/details/81490297