android 树形目录结构的实现(包含源码)

这里写图片描述

这里写图片描述
1.效果如图 ,老板的需求:

1.各种群按照树形目录展开
2.点击列表可以收缩展开
3.选中后进行筛选

2.分析下思路

  1. 每个数据都应该有个根节点,也就是上级目录。确定了上级目录就可以找到当前数据对应的位置。
    2.为避免多重嵌套影响性能,这里的数据展示我们用一个列表足矣实现
    3.实现的思路是item向右偏移

3.贴代码时间

Node节点对象的定义 项目中用到了环信SDK 环信部分直接忽略吧 QAQ

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Node implements Serializable{


    /**
     * 节点id
     */
    private int id;
    /**
     * 父节点id
     */
    private int pId;
    /**
     * 是否展开
     */
    private boolean isExpand = false;
    private boolean isChecked = false;
    private boolean isHideChecked = true;
    /**
     * 节点名字
     */
    private String name;
    /**
     * 节点级别
     */
    private int level;
    /**
     * 节点展示图标
     */
    private int icon;
    /**
     * 节点所含的子节点
     */
    private List<Node> childrenNodes = new ArrayList<Node>();
    /**
     * 节点的父节点
     */
    private Node parent;
    /**
     * 环信群ID
     */
    private String hxGroupId;

    public Node() {
    }

    public Node(int id, int pId, String name,String hxGroupId) {
        super();
        this.id = id;
        this.pId = pId;
        this.name = name;
        this.hxGroupId = hxGroupId;
    }

    public String getHxGroupId() {
        return hxGroupId;
    }

    public void setHxGroupId(String hxGroupId) {
        this.hxGroupId = hxGroupId;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", pId=" + pId +
                ", isExpand=" + isExpand +
                ", isChecked=" + isChecked +
                ", isHideChecked=" + isHideChecked +
                ", name='" + name + '\'' +
                ", level=" + level +
                ", icon=" + icon +
                ", childrenNodes=" + childrenNodes +
                ", parent=" + parent +
                ", hxGroupId='" + hxGroupId + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getpId() {
        return pId;
    }

    public void setpId(int pId) {
        this.pId = pId;
    }

    public boolean isExpand() {
        return isExpand;
    }

    /**
     * 当父节点收起,其子节点也收起
     * @param isExpand
     */
    public void setExpand(boolean isExpand) {
        this.isExpand = isExpand;
        if (!isExpand) {

            for (Node node : childrenNodes) {
                node.setExpand(isExpand);
            }
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLevel() {
        return parent == null ? 0 : parent.getLevel() + 1;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public List<Node> getChildrenNodes() {
        return childrenNodes;
    }

    public void setChildrenNodes(List<Node> childrenNodes) {
        this.childrenNodes = childrenNodes;
    }

    public Node getParent() {
        return parent;
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    /**
     * 判断是否是根节点
     * 
     * @return
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * 判断是否是叶子节点
     * 
     * @return
     */
    public boolean isLeaf() {
        return childrenNodes.size() == 0;
    }


    /**
     * 判断父节点是否展开
     * 
     * @return
     */
    public boolean isParentExpand()
    {
        if (parent == null)
            return false;
        return parent.isExpand();
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public boolean isHideChecked() {
        return isHideChecked;
    }

    public void setHideChecked(boolean isHideChecked) {
        this.isHideChecked = isHideChecked;
    }
}

帮助类TreeHelper

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class TreeHelper {

    /**
     * 根据所有节点获取可见节点
     * 
     * @param allNodes
     * @return
     */
    public static List<Node> filterVisibleNode(List<Node> allNodes) {
        List<Node> visibleNodes = new ArrayList<Node>();
        for (Node node : allNodes) {
            // 如果为根节点,或者上层目录为展开状态
            if (node.isRoot() || node.isParentExpand()) {
                setNodeIcon(node);
                visibleNodes.add(node);
            }
        }
        return visibleNodes;
    }

    /**
     * 获取排序的所有节点
     * 
     * @param datas
     * @param defaultExpandLevel
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> List<Node> getSortedNodes(List<T> datas,
            int defaultExpandLevel, boolean isHide)
            throws IllegalAccessException, IllegalArgumentException {
        List<Node> sortedNodes = new ArrayList<Node>();
        // 将用户数据转化为List<Node>
        List<Node> nodes = convertData2Nodes(datas, isHide);
        // 拿到根节点
        List<Node> rootNodes = getRootNodes(nodes);
        // 排序以及设置Node间关系
        for (Node node : rootNodes) {
            addNode(sortedNodes, node, defaultExpandLevel, 1);
        }
        return sortedNodes;
    }

    /**
     * 把一个节点上的所有的内容都挂上去
     */
    private static void addNode(List<Node> nodes, Node node,
            int defaultExpandLeval, int currentLevel) {

        nodes.add(node);
        if (defaultExpandLeval >= currentLevel) {
            node.setExpand(true);
        }

        if (node.isLeaf())
            return;
        for (int i = 0; i < node.getChildrenNodes().size(); i++) {
            addNode(nodes, node.getChildrenNodes().get(i), defaultExpandLeval,
                    currentLevel + 1);
        }
    }

    /**
     * 获取所有的根节点
     * 
     * @param nodes
     * @return
     */
    public static List<Node> getRootNodes(List<Node> nodes) {
        List<Node> rootNodes = new ArrayList<Node>();
        for (Node node : nodes) {
            if (node.isRoot()) {
                rootNodes.add(node);
            }
        }

        return rootNodes;
    }

    /**
     * 将泛型datas转换为node
     * 
     * @param datas
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> List<Node> convertData2Nodes(List<T> datas, boolean isHide)
            throws IllegalAccessException, IllegalArgumentException {
        List<Node> nodes = new ArrayList<Node>();
        Node node = null;

        for (T t : datas) {
            int id = -1;
            int pId = -1;
            String name = null;
            String hxGroupId = null;

            Class<? extends Object> clazz = t.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            /**
             * 与MyNodeBean实体一一对应
             */
            for (Field f : declaredFields) {
                if ("id".equals(f.getName())) {
                    f.setAccessible(true);
                    id = f.getInt(t);
                }

                if ("pId".equals(f.getName())) {
                    f.setAccessible(true);
                    pId = f.getInt(t);
                }

                if ("name".equals(f.getName())) {
                    f.setAccessible(true);
                    name = (String) f.get(t);
                }

                if ("hxGroupId".equals(f.getName())) {
                    f.setAccessible(true);
                    hxGroupId = (String) f.get(t);
                }

                if ("desc".equals(f.getName())) {
                    continue;
                }

                if ("length".equals(f.getName())) {
                    continue;
                }



                if (id == -1 && pId == -1 && name == null&&hxGroupId==null) {
                    break;
                }
            }

            node = new Node(id, pId, name,hxGroupId);
            node.setHideChecked(isHide);
            nodes.add(node);
        }

        /**
         * 比较nodes中的所有节点,分别添加children和parent
         */
        for (int i = 0; i < nodes.size(); i++) {
            Node n = nodes.get(i);
            for (int j = i + 1; j < nodes.size(); j++) {
                Node m = nodes.get(j);
                if (n.getId() == m.getpId()) {
                    n.getChildrenNodes().add(m);
                    m.setParent(n);
                } else if (n.getpId() == m.getId()) {
                    n.setParent(m);
                    m.getChildrenNodes().add(n);
                }
            }
        }

        for (Node n : nodes) {
            setNodeIcon(n);
        }
        return nodes;
    }

    /**
     * 设置打开,关闭icon
     * 
     * @param node
     */
    public static void setNodeIcon(Node node) {
        if (node.getChildrenNodes().size() > 0 && node.isExpand()) {
            node.setIcon(R.drawable.arrow_right);
        } else if (node.getChildrenNodes().size() > 0 && !node.isExpand()) {
            node.setIcon(R.drawable.arrow_down);
        } else
            node.setIcon(-1);
    }

    public static void setNodeChecked(Node node, boolean isChecked) {
        // 自己设置是否选择
        node.setChecked(isChecked);
        /**
         * 非叶子节点,子节点处理
         */
        setChildrenNodeChecked(node, isChecked);
        /** 父节点处理 */
        setParentNodeChecked(node);

    }

    /**
     * 非叶子节点,子节点处理
     */
    private static void setChildrenNodeChecked(Node node, boolean isChecked) {
        node.setChecked(isChecked);
        if (!node.isLeaf()) {
            for (Node n : node.getChildrenNodes()) {
                // 所有子节点设置是否选择
                setChildrenNodeChecked(n, isChecked);
            }
        }
    }

    /**
     * 设置父节点选择
     * 
     * @param node
     */
    private static void setParentNodeChecked(Node node) {

        /** 非根节点 */
        if (!node.isRoot()) {
            Node rootNode = node.getParent();
            boolean isAllChecked = true;
            for (Node n : rootNode.getChildrenNodes()) {
                if (!n.isChecked()) {
                    isAllChecked = false;
                    break;
                }
            }

            if (isAllChecked) {
                rootNode.setChecked(true);
            } else {
                rootNode.setChecked(false);
            }
            setParentNodeChecked(rootNode);
        }
    }

}

适配器TreeListViewAdapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.RelativeLayout;

import java.util.ArrayList;
import java.util.List;

/**
 * tree适配器
 * @param <T>
 */
public abstract class TreeListViewAdapter<T> extends BaseAdapter {

    protected Context mContext;
    /**
     * 存储所有可见的Node
     */
    protected List<Node> mNodes;
    protected LayoutInflater mInflater;
    /**
     * 存储所有的Node
     */
    protected List<Node> mAllNodes;

    /**
     * 点击的回调接口
     */
    private OnTreeNodeClickListener onTreeNodeClickListener;

    public interface OnTreeNodeClickListener {
        /**
         * 处理node click事件
         * @param node
         * @param position
         */
        void onClick(Node node, int position);
        /**
         * 处理checkbox选择改变事件
         * @param node
         * @param position
         * @param checkedNodes
         */
        void onCheckChange(Node node, int position, List<Node> checkedNodes);
    }

    public void setOnTreeNodeClickListener(
            OnTreeNodeClickListener onTreeNodeClickListener) {
        this.onTreeNodeClickListener = onTreeNodeClickListener;
    }

    /**
     * 
     * @param mTree
     * @param context
     * @param datas
     * @param defaultExpandLevel
     *            默认展开几级树
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public TreeListViewAdapter(ListView mTree, Context context, List<T> datas,
            int defaultExpandLevel, boolean isHide)
            throws IllegalArgumentException, IllegalAccessException {
        mContext = context;
        /**
         * 对所有的Node进行排序
         */
        mAllNodes = TreeHelper
                .getSortedNodes(datas, defaultExpandLevel, isHide);
        /**
         * 过滤出可见的Node
         */
        mNodes = TreeHelper.filterVisibleNode(mAllNodes);
        mInflater = LayoutInflater.from(context);

        /**
         * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
         */
        mTree.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                expandOrCollapse(position);

                if (onTreeNodeClickListener != null) {
                    onTreeNodeClickListener.onClick(mNodes.get(position),
                            position);
                }
            }

        });

    }

    /**
     * 相应ListView的点击事件 展开或关闭某节点
     * 
     * @param position
     */
    public void expandOrCollapse(int position) {
        Node n = mNodes.get(position);

        if (n != null)// 排除传入参数错误异常
        {
            if (!n.isLeaf()) {
                n.setExpand(!n.isExpand());
                mNodes = TreeHelper.filterVisibleNode(mAllNodes);
                notifyDataSetChanged();// 刷新视图
            }
        }
    }

    @Override
    public int getCount() {
        return mNodes.size();
    }

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

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final Node node = mNodes.get(position);

        convertView = getConvertView(node, position, convertView, parent);
        // 设置内边距
        convertView.setPadding(node.getLevel() * 56, 3, 3, 3);
        if (!node.isHideChecked()) {
            //获取各个节点所在的父布局
            RelativeLayout myView = (RelativeLayout) convertView;
            //父布局下的CheckBox
            CheckBox cb = (CheckBox) myView.getChildAt(0);
            //扩充可触摸的区域
            //expandViewTouchDelegate(cb,50,50,50,50);
            cb.setOnCheckedChangeListener(new OnCheckedChangeListener(){

                @Override
                public void onCheckedChanged(CompoundButton buttonView,
                        boolean isChecked) {
                    TreeHelper.setNodeChecked(node, isChecked);
                    List<Node> checkedNodes = new ArrayList<Node>();
                    for(Node n:mAllNodes){
                        if(n.isChecked()){
                            checkedNodes.add(n);
                        }
                    }

                    onTreeNodeClickListener.onCheckChange(node,position,checkedNodes);
                    TreeListViewAdapter.this.notifyDataSetChanged();
                }

            });
        }

        return convertView;
    }

    public abstract View getConvertView(Node node, int position,
            View convertView, ViewGroup parent);

    /**
     * 更新
     * @param isHide
     */
    public void updateView(boolean isHide){
        for(Node node:mAllNodes){
            node.setHideChecked(isHide);
        }

        this.notifyDataSetChanged();
    }

activity中的主要代码

private void initView() {
        initTreeData();
        try {
            adapter = new MyTreeListViewAdapter<>(treeLv, this,
                    mDatas, 0, isHide);

            adapter.setOnTreeNodeClickListener(new TreeListViewAdapter.OnTreeNodeClickListener() {

                @Override
                public void onClick(Node node, int position) {
                    if (node.isLeaf()) {

                    }
                }

                @Override
                public void onCheckChange(Node node, int position,
                                          List<Node> checkedNodes) {
                    // TODO Auto-generated method stub
                    checkedNode = checkedNodes;
                    StringBuffer sb = new StringBuffer();
                    for (Node n : checkedNodes) {
                        sb.append(n.getName()).append(":").append(n.getHxGroupId());
                    }
                    Logger.d("选中的" + sb.toString());
                }

            });
            adapter.updateView(isHide);
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        treeLv.setAdapter(adapter);

    }

    private void initTreeData() {
        for (ChatGroupEntity.RDataBean bean : countTask.getRData()) {
            mDatas.add(new MyNodeBean(Integer.valueOf(bean.getGroup_id()), Integer.valueOf(bean.getUp_group_id()), bean.getGroup_code(), bean.getHx_id()));
        }
    }

布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:focusable="true"
              android:focusableInTouchMode="true"
              android:layout_height="match_parent">
    <include layout="@layout/title_bar_common"/>
    <com.fjrcloud.fuqing.util.customview.ClearEditText
        android:id="@+id/filter_edit"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:focusableInTouchMode="true"
        android:layout_marginTop="8dip"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:gravity="center_vertical"
        android:layout_marginBottom="8dp"
        android:paddingLeft="15dp"
        android:drawablePadding="15dp"
        android:background="@drawable/shape_search_view"
        android:drawableLeft="@mipmap/icon_check_name"
        android:hint="搜索"
        android:singleLine="true"
        android:textSize="14sp"/>
    <TextView
        android:background="#f1f1f1"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_15"/>

    <ListView
        android:layout_marginTop="15dp"
        android:id="@+id/tree_lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>


</LinearLayout>

OK收工。有什么不懂得可以留言 联系我QQ :910689331
或者微博:Liberation_k

当然 源码在这哈
链接:http://pan.baidu.com/s/1c0KpS6 密码:q4xf

发布了53 篇原创文章 · 获赞 17 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq910689331/article/details/78271011
今日推荐