Android实现树状多层可展开收起ListView

来源:互联网 发布:购买0 信誉淘宝店铺 编辑:程序博客网 时间:2024/05/20 20:56

最近项目用到树状ListView,要可展开收起。Android自带的ExpandableListView不太适合扩展,看了网上一些实现,发现通用性不是很好,于是参考可取之处,自己写了一个比较通过的实现。效果如下:

 

1、首先是数据模型。

public class Node<T extends Node> implements Comparable<Node> {    /** 自己的id */    public int id;    /** 上一层id */    public int pId;    /** 层级 */    public int level;    /** 是否展开 */    public boolean isExpand;    /** 子节点 */    public List<T> childNodes;    public Node() {    }    public Node(int id, int pId, int level, boolean isExpand) {        this.id = id;        this.pId = pId;        this.level = level;        this.isExpand = isExpand;    }    @Override    public int compareTo(@NonNull Node o) {        return id - o.id;    }        public boolean hasChild() {        return childNodes != null && !childNodes.isEmpty();    }        public void addChild(T node) {        if (childNodes == null) {            childNodes = new ArrayList<>();        }        childNodes.add(node);    }}
2、然后就是ListView的数据适配器了。

public abstract class TreeAdapter<T extends Node<T>> extends BaseAdapter {    private List<T> totalNodes = new ArrayList<>();    private List<T> showNodes = new ArrayList<>();    private List<T> firstLevelNodes = new ArrayList<>();    private SparseIntArray addedChildNodeIds = new SparseIntArray();    private OnInnerItemClickListener<T> listener;    private OnInnerItemLongClickListener<T> longListener;    public interface OnInnerItemClickListener<T> {        void onClick(T node);    }        public interface OnInnerItemLongClickListener<T> {        void onLongClick(T node);    }        public TreeAdapter(List<T> nodes) {        setNodes(nodes);            }        public void setOnInnerItemClickListener(OnInnerItemClickListener<T> listener) {        this.listener = listener;    }        public void setOnInnerItemLongClickListener(OnInnerItemLongClickListener<T> listener) {        longListener = listener;    }        public void setNodes(List<T> nodes) {        if (nodes != null) {            totalNodes = nodes;            //过滤出显示的节点            init();            super.notifyDataSetChanged();        }    }        private void init() {        showNodes.clear();        initNodes();        addedChildNodeIds.clear();        showNodes.addAll(firstLevelNodes);        filterShowAndSortNodes();    }    @Override    public void notifyDataSetChanged() {        init();        super.notifyDataSetChanged();    }    private void initNodes() {        firstLevelNodes.clear();        //先循环一次,获取最小的level        Integer level = null;        for (T node : totalNodes) {            if (level == null || level > node.level) {                level = node.level;            }        }        for (T node : totalNodes) {            //过滤出最外层            if (node.level == level) {                firstLevelNodes.add(node);            }            //清空之前添加的            if (node.hasChild()) {                node.childNodes.clear();            }            //给节点添加子节点并排序            for (T t : totalNodes) {                if (node.id == t.id && node != t) {                    throw new IllegalArgumentException("id cannot be duplicated");                }                if (node.id == t.pId && node.level != t.level) {                    node.addChild(t);                }            }            if (node.hasChild()) {                Collections.sort(node.childNodes);            }        }            Collections.sort(firstLevelNodes);    }        private void filterShowAndSortNodes() {        for (int i = 0; i < showNodes.size(); i++) {            T node = showNodes.get(i);            int value = addedChildNodeIds.get(node.id);            if (value == 0 && node.isExpand && node.hasChild()) {                List<T> list = new ArrayList<>(showNodes);                list.addAll(i + 1, node.childNodes);                showNodes = list;                addedChildNodeIds.put(node.id, 1);                filterShowAndSortNodes();                break;            }        }    }    @Override    public int getCount() {        return showNodes.size();    }    @Override    public T getItem(int position) {        return showNodes.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        Holder<T> holder;        if (convertView == null) {            holder = getHolder(position);        } else {            holder = (Holder<T>) convertView.getTag();        }        T node = showNodes.get(position);        holder.setData(node);        holder.position = position;        View view = holder.getConvertView();        view.setOnClickListener(clickListener);        if (!node.hasChild()) {            view.setOnLongClickListener(longClickListener);        }        return view;    }        public abstract static class Holder<T> {        private View convertView;        int position;        public Holder() {            convertView = createConvertView();            convertView.setTag(this);        }        public View getConvertView() {            return convertView;        }                /**         * 设置数据         */        protected abstract void setData(T node);        /**         * 创建界面         */        protected abstract View createConvertView();    }    protected abstract Holder<T> getHolder(int position);        private View.OnClickListener clickListener = new View.OnClickListener() {        @Override        public void onClick(View v) {            Holder<T> holder = (Holder<T>) v.getTag();            T node = showNodes.get(holder.position);            if (node.hasChild()) {                node.isExpand = !node.isExpand;                if (!node.isExpand) {                    fold(node.childNodes);                }                showNodes.clear();                addedChildNodeIds.clear();                showNodes.addAll(firstLevelNodes);                filterShowAndSortNodes();                TreeAdapter.super.notifyDataSetChanged();            } else if (listener != null) {                listener.onClick(node);            }                        }    };        private View.OnLongClickListener longClickListener = new View.OnLongClickListener() {        @Override        public boolean onLongClick(View v) {            if (longListener != null) {                Holder<T> holder = (Holder<T>) v.getTag();                longListener.onLongClick(showNodes.get(holder.position));            }            return true;        }    };        //递归收起节点及子节点    private void fold(List<T> list) {        for (T t : list) {            t.isExpand = false;            if (t.hasChild()) {                fold(t.childNodes);            }        }    }}
3、以上两个文件直接写成成库,然后就可以使用了。

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ListView lv = new ListView(this);        setContentView(lv, new ViewGroup.LayoutParams(-1, -1));        final List<Item> list = new ArrayList<>();        list.add(new Item(0, 0, 0, false, "Android"));        list.add(new Item(1, 0, 1, false, "Service"));        list.add(new Item(2, 0, 1, false, "Activity"));        list.add(new Item(3, 0, 1, false, "Receiver"));        list.add(new Item(4, 0, 0, true, "Java Web"));        list.add(new Item(5, 4, 1, false, "CSS"));        list.add(new Item(6, 4, 1, false, "Jsp"));        list.add(new Item(7, 4, 1, true, "Html"));        list.add(new Item(8, 7, 2, false, "p"));        final MyAdapter adapter = new MyAdapter(list);        adapter.setOnInnerItemClickListener(new TreeAdapter.OnInnerItemClickListener<Item>() {            @Override            public void onClick(Item node) {                Toast.makeText(MainActivity.this, "click: " + node.name, Toast.LENGTH_SHORT).show();            }        });        adapter.setOnInnerItemLongClickListener(new TreeAdapter.OnInnerItemLongClickListener<Item>() {            @Override            public void onLongClick(Item node) {                Toast.makeText(MainActivity.this, "long click: " + node.name, Toast.LENGTH_SHORT).show();            }        });        lv.setAdapter(adapter);        new Handler().postDelayed(new Runnable() {            @Override            public void run() {                list.add(new Item(9, 7, 2, false, "a"));                adapter.notifyDataSetChanged();            }        }, 2000);    }    private class MyAdapter extends TreeAdapter<Item> {        MyAdapter(List<Item> nodes) {            super(nodes);        }        @Override        public int getViewTypeCount() {            return super.getViewTypeCount() + 1;        }        /**         * 获取当前位置的条目类型         */        @Override        public int getItemViewType(int position) {            if (getItem(position).hasChild()) {                return 1;            }            return 0;        }                @Override        protected Holder<Item> getHolder(int position) {            switch(getItemViewType(position)) {                case 1:                    return new Holder<Item>() {                        private ImageView iv;                        private TextView tv;                        @Override                        protected void setData(Item node) {                            iv.setVisibility(node.hasChild() ? View.VISIBLE : View.INVISIBLE);                            iv.setBackgroundResource(node.isExpand ? R.mipmap.expand : R.mipmap.fold);                            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) iv.getLayoutParams();                            params.leftMargin = (node.level + 1) * dip2px(20);                            iv.setLayoutParams(params);                            tv.setText(node.name);                        }                        @Override                        protected View createConvertView() {                            View view = View.inflate(MainActivity.this, R.layout.item_tree_list_has_child, null);                            iv = (ImageView) view.findViewById(R.id.ivIcon);                            tv = (TextView) view.findViewById(R.id.tvName);                            return view;                        }                    };                default:                    return new Holder<Item>() {                        private TextView tv;                                                @Override                        protected void setData(Item node) {                            tv.setText(node.name);                            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) tv.getLayoutParams();                            params.leftMargin = (node.level + 3) * dip2px(20);                            tv.setLayoutParams(params);                        }                        @Override                        protected View createConvertView() {                            View view = View.inflate(MainActivity.this, R.layout.item_tree_list_no_child, null);                            tv = (TextView) view.findViewById(R.id.tvName);                            return view;                        }                    };            }        }    }    /**     * 根据手机的分辨率从 dip 的单位 转成为 px(像素)      */    public int dip2px(float dpValue) {        final float scale = getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }        private class Item extends Node<Item> {        String name;        Item(int id, int pId, int level, boolean isExpand, String name) {            super(id, pId, level, isExpand);            this.name = name;        }    }}

源码:https://github.com/fszeng2011/treeadapter


原创粉丝点击