本文参考Hongyang大神文章,自己写个demo记录下。

先看效果图:

Android 左边不能树状显示 android 树状图_树状结构


总思路:

将每条数据都看作是listview的一个item,按照它们之间的关系,将这些item由上到下排序。然后将每个item设置为不同的显示状态,再加上点击每个item会联动其他相应item的显示状态改变,这样就达到了树状结构导航的目的。

1、将每条原始数据都转化为节点数据,因为每个节点数据里有:
a、子节点、父节点:这个很重要,因为每条数据的缩进、图标的设置、是否展示,都要依据每条数据间的父子关系来进行判断展示;
b、左边距的空格数:设置缩进,以展现树状结构效果
c、展开的状态:说明此节点目前是否展开

2、在适配器配置这条节点数据的View时,依据这条节点数据的各个属性,来做不同的展示,以达到最终树状结构展示效果的目的。


写完这个demo,让我深刻的认识到了一个道理:
每个控件其实都对应一条数据,“控件展示状态的改变”本质上其实就是“它所对应的那条数据中的某个参数改变了”而已。


原始数据Bean:

public class Bean {
    @TreeId
    int id;
    @TreePid
    int pid;
    @TreeLabel
    String name;

    public Bean(int id, int pid, String name) {
        this.id = id;
        this.pid = pid;
        this.name = name;
    }
}

节点数据Node:

public class Node {

    public Node(int id, int pid, String name) {
        this.id = id;
        this.pid = pid;
        this.name = name;
    }

    int id;
    int pid;
    String name;
    /**
     * 子节点
     */
    List<Node> children = new ArrayList<>();
    /**
     * 父节点
     */
    Node parent;
    /**
     * 前面有几个单位的空格
     */
    int level;
    /**
     * 是否是展开状态
     */
    boolean isExpand = false;

    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 List<Node> getChildren() {
        return children;
    }

    public Node getParent() {
        return parent;
    }

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

    public String getName() {
        return name;
    }

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

    public int getLevel() {
        return level;
    }

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

    public void setExpand(boolean expand) {
        isExpand = expand;
    }
}

Activity:

public class MainActivity extends Activity {
    /**
     * 原始数据
     */
    List<Bean> beans = new ArrayList<>();
    /**
     * 由原始数据转化的节点数据
     */
    List<Node> nodes = new ArrayList<>();
    /**
     * 经过排序的节点数据
     */
    List<Node> sortedNodes = new ArrayList<>();
    /**
     * 适配器
     */
    TreeListViewAdapter adapter;
    /**
     * listview
     */
    ListView lv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lv = (ListView) findViewById(R.id.lv);

        //初始化原始数据
        beans.add(new Bean(10, 7, "东北人"));
        beans.add(new Bean(4, 1, "人"));
        beans.add(new Bean(12, 8, "广东人"));
        beans.add(new Bean(2, 1, "猴子"));
        beans.add(new Bean(5, 2, "金丝猴"));
        beans.add(new Bean(7, 4, "北方人"));
        beans.add(new Bean(3, 1, "龙"));
        beans.add(new Bean(1, 0, "动物"));
        beans.add(new Bean(9, 7, "内蒙人"));
        beans.add(new Bean(6, 2, "猕猴"));
        beans.add(new Bean(11, 7, "北京人"));
        beans.add(new Bean(8, 4, "南方人"));

        try {
            //将原始数据转化为节点数据
            nodes = TreeHelper.data2Node(beans);
            //将节点数据排序
            sortedNodes = TreeHelper.sort(nodes);
            //将排好顺序的节点数据传入适配器
            adapter = new TreeListViewAdapter(this, sortedNodes);
            //展示数据
            lv.setAdapter(adapter);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

TreeListViewAdapter:

public class TreeListViewAdapter extends BaseAdapter {

    List<Node> datas;
    Context context;

    public TreeListViewAdapter(Context context, List<Node> datas) {
        this.datas = datas;
        this.context = context;
    }

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

    @Override
    public Object getItem(int i) {
        return null;
    }

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        final Node node = datas.get(i);
        ViewHolder viewHolder;
        if (view == null) {
            view = LayoutInflater.from(context).inflate(R.layout.item, null);
            viewHolder = new ViewHolder();
            viewHolder.tv = (TextView) view.findViewById(R.id.tv);
            viewHolder.iv = (ImageView) view.findViewById(R.id.iv);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }
        //显示文字
        viewHolder.tv.setText(node.getName());

        //点击节点时
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //如果节点当前状态是展开,那么将它的所有子孙节点都设置为关闭状态
                if(node.isExpand==true){
                    TreeHelper.shutNode(node);
                }
                //将节点的展开状态改为相反
                node.setExpand(!node.isExpand);
                //刷新显示
                notifyDataSetChanged();
            }
        });

        //如果此节点有子节点
        if(node.children.size()>0){
            //节点状态是展开时,设置相应图标
            if(node.isExpand == true){
                viewHolder.iv.setImageResource(R.drawable.tree_ec);
                //节点状态是关闭时,设置相应图标
            }else {
                viewHolder.iv.setImageResource(R.drawable.tree_ex);
            }
            //如果此节点无子节点,设置图片为空
        }else {
            viewHolder.iv.setImageBitmap(null);
        }

        //设置此条item的左边距
        view.setPadding(node.getLevel()*50,0,0,0);

        //如果此节点为根节点或者它的父节点为展开状态,那么显示该节点;
        //否则,不显示此节点。
        Node parentNode = node.getParent();
        if (parentNode == null || parentNode.isExpand == true) {
            return view;
        } else{
            return null;
        }

    }

    class ViewHolder {
        ImageView iv;
        TextView tv;
    }
}

TreeHelper:

public class TreeHelper {
    /**
     * 将原始数据转为节点数据
     * @param datas
     * @return
     * @throws IllegalAccessException
     */
    public static List<Node> data2Node(List<Bean> datas) throws IllegalAccessException {
        List<Node> nodes = new ArrayList<>();
        Node node = null;
        //根据反射将数据源里的id、pid、name拿出来
        for (Bean t : datas) {
            int id = -1;
            int pid = -1;
            String label = null;
            Class<? extends Object> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field f : fields) {
                if (f.getAnnotation(TreeId.class) != null) {
                    id = f.getInt(t);
                }
                if (f.getAnnotation(TreePid.class) != null) {
                    pid = f.getInt(t);
                }
                if (f.getAnnotation(TreeLabel.class) != null) {
                    label = (String) f.get(t);
                }
                if (id != -1 && pid != -1 && label != null) {
                    break;
                }
            }
            node = new Node(id, pid, label);
            nodes.add(node);
        }

        //遍历每一个节点及其之后的节点。会出现两种情况,
        // 这个节点是之后某个节点的父节点
        //这个节点是之后某个节点的子节点
        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.getChildren().add(m);
                    m.setParent(n);
                }
                if (n.getPid() == m.getId()) {
                    n.setParent(m);
                    m.getChildren().add(n);
                }
            }
        }

        //设置此节点左边距应该设置几个空格
        for (Node n : nodes) {
            n.setLevel(calPadding(n,0));
        }
        return nodes;
    }

    /**
     * 将节点数据按照父子级关系排序
     * @param nodes
     * @return
     */
    public static List<Node> sort(List<Node> nodes) {
        //先将根节点选出来
        List<Node> rootNodes = new ArrayList<>();
        for (Node n : nodes) {
            if (n.getParent() == null) {
                rootNodes.add(n);
            }
        }
        //按照父子关系将节点一个个放进去,以达到排序目的
        List<Node> sortedNodes = new ArrayList<>();
        addNode(sortedNodes, rootNodes);
        return sortedNodes;
    }

    /**
     * 排序节点的具体实现方法
     * @param sortedNodes
     * @param rootNodes
     */
    private static void addNode(List<Node> sortedNodes, List<Node> rootNodes) {
        for (Node n : rootNodes) {
            sortedNodes.add(n);
            if (n.children.size() > 0) {
                addNode(sortedNodes,n.children);
            }
        }
    }

    /**
     * 将一个节点的所有子节点的展开状态都设置为关闭
     * @param n
     */
    public static void shutNode(Node n){
        List<Node> children = n.getChildren();
        if(children.size()>0){
            for (Node n1 : children) {
                n1.setExpand(false);
                shutNode(n1);
            }
        }
    }

    /**
     * 计算此节点左边距的空格个数
     * @param node
     * @param i
     * @return
     */
    public static int calPadding(Node node,int i){
        if(node.parent!=null){
            i++;
            i=calPadding(node.parent,i);
        }
        return i;
    }
}