android多级树形结构列表(完整代码,可以选择某项)
发布日期:2021-05-08 04:55:04 浏览次数:15 分类:原创文章

本文共 13195 字,大约阅读时间需要 43 分钟。

多级列表是个很常见的功能,发现很多代码都不好扩展,或者由于数据结构设计不好,导致开发维护比较费时间。
11月14号更新:增加了 选择按钮,可以实现选中效果。用于选择。
下面自己写了一个,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;    }}

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

上一篇:成功解决Keil MDK5中no browse information available in ‘xxx’的问题
下一篇:自定义LoadingView

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月08日 06时04分25秒