【笔记】Android使用RecyclerView实现可自定义节点的树形列表

13 篇文章 0 订阅

树形列表的基本功能是可以展开收拢,该列表还可以响应选中、单项点击事件。使用者可以自定义需要的各级节点的布局样式,在适配器的展开、选中和单项点击回调中实现需要的效果。

效果展示


使用说明

在项目build.gradle中添加

maven { url "https://jitpack.io" }

添加引用(可在 github获取最新版本)

compile 'com.github.q1113225201:TreeView:1.0.2'
实现节点对象和节点对象绑定类, 我这边实现了跟枝页三种不同节点,使用时可以根据需求去实现自己需要的节点对象和布局


初始化树列表,真正显示的列表只需要显示跟节点列表,枝节点添加到根节点下,叶节点添加到枝节点下,也可以直接添加子节点列表


添加到RecyclerView中


源码分析

节点布局接口,因为有了这个类才能实现自定义每个节点的布局,自己实现展开、选中、单项点击的效果

public interface LayoutItem {
    /**
     * 返回布局id
     * @return
     */
    int getLayoutId();

    /**
     * 返回展开收拢事件触发id
     * @return
     */
    int getToggleId();

    /**
     * 返回选中事件触发id
     * @return
     */
    int getCheckedId();

    /**
     * 返回单项点击事件触发id
     * @return
     */
    int getClickId();
}

自定义的节点对象继承TreeNode,自定义的数据都放在value属性中,该对象保存有当前节点父节点、子节点列表、展开状态、选中状态和所在树层级

public class TreeNode<T extends LayoutItem> {
    /**
     * 当前节点值
     */
    private T value;
    /**
     * 父节点
     */
    private TreeNode parentNode;
    /**
     * 孩子节点列表
     */
    private List<TreeNode> childNodes = new ArrayList<>();
    /**
     * 是否已展开
     */
    private boolean expanded;
    /**
     * 是否被选中
     */
    private boolean checked;
    /**
     * 层级
     */
    private int level = 0;

    public TreeNode(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public TreeNode getParentNode() {
        return parentNode;
    }

    public void setParentNode(TreeNode parentNode) {
        this.parentNode = parentNode;
    }

    public List<TreeNode> getChildNodes() {
        return childNodes;
    }

    public void setChildNodes(List<TreeNode> childNodes) {
        if (childNodes == null) {
            childNodes = new ArrayList<>();
        }
        for (int i = 0; i < childNodes.size(); i++) {
            childNodes.get(i).setParentNode(this);
        }
        initLevel(childNodes);
        this.childNodes = childNodes;
    }

    public boolean isExpanded() {
        return expanded;
    }

    public void setExpanded(boolean expanded) {
        this.expanded = expanded;
    }

    public boolean isChecked() {
        return checked;
    }

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

    public int getLevel() {
        return level;
    }

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

    public boolean toggle() {
        expanded = !expanded;
        return expanded;
    }

    public boolean open() {
        expanded = true;
        return expanded;
    }

    public boolean close() {
        expanded = false;
        return expanded;
    }

    public boolean isRoot() {
        return parentNode == null;
    }

    public boolean isLeaf() {
        return childNodes == null || childNodes.isEmpty();
    }

    public void addChild(TreeNode treeNode) {
        if (childNodes == null) {
            childNodes = new ArrayList<>();
        }
        childNodes.add(treeNode);
        initLevel(childNodes);
    }

    private void initLevel(List<TreeNode> childNodes) {
        for (TreeNode item : childNodes) {
            item.setLevel(item.getParentNode().getLevel() + 1);
            if (!item.getChildNodes().isEmpty()) {
                initLevel(item.childNodes);
            }
        }

    }

    @Override
    public boolean equals(Object obj) {
        TreeNode treeNode = (TreeNode) obj;
        return value.equals(treeNode.getValue())
                && ((parentNode != null && parentNode.equals(treeNode.getParentNode())) || (parentNode == null && treeNode.getParentNode() == null))
                && childNodes.equals(treeNode.getChildNodes())
                && expanded == treeNode.isExpanded()
                && checked == treeNode.isChecked();
    }
}
树形节点绑定抽象,该类有创建视图和绑定视图抽象方法,使用过RecyclerView的应该都非常熟悉

public abstract class TreeViewBinder<VH extends RecyclerView.ViewHolder> implements LayoutItem {
    /**
     * 创建视图
     * @param itemView
     * @return
     */
    public abstract VH creatViewHolder(View itemView);

    /**
     * 绑定视图
     * @param holder
     * @param position
     * @param treeNode
     */
    public abstract void bindViewHolder(VH holder, int position, TreeNode treeNode);

    public class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);
        }

        public <T extends View> T findViewById(int id) {
            return itemView.findViewById(id);
        }
    }
}
适配器中实现了节点展开和收拢时列表的变化,选中效果的改变也是通过RecyclerView的局部更新实现的

    /**
     * 节点展开收拢
     *
     * @param currentNode
     */
    public void toggle(TreeNode currentNode) {
        boolean isExpanded = currentNode.isExpanded();
        int startPosition = expandedList.indexOf(currentNode) + 1;
        if (isExpanded) {
            notifyItemRangeRemoved(startPosition, removeNodes(currentNode, true));
        } else {
            notifyItemRangeInserted(startPosition, insertNodes(currentNode, startPosition));
        }
    }

    /**
     * 收拢时移除节点
     *
     * @param treeNode
     * @param toggle
     * @return
     */
    private int removeNodes(TreeNode treeNode, boolean toggle) {
        int count = 0;
        if (!treeNode.isLeaf()) {
            List<TreeNode> list = treeNode.getChildNodes();
            count += list.size();
            expandedList.removeAll(list);
            for (TreeNode item : list) {
                if (item.isExpanded()) {
                    item.toggle();
                }
                count += removeNodes(item, false);
            }
        }
        if (toggle) {
            treeNode.toggle();
        }
        return count;
    }

    /**
     * 展开时插入节点
     *
     * @param treeNode
     * @param startPosition
     * @return
     */
    private int insertNodes(TreeNode treeNode, int startPosition) {
        List<TreeNode> list = treeNode.getChildNodes();
        int count = 0;
        for (TreeNode item : list) {
            expandedList.add(startPosition + count, item);
            count++;
            if (item.isExpanded()) {
                count += insertNodes(item, startPosition + count);
            }
        }
        if (!treeNode.isExpanded()) {
            treeNode.setExpanded(!treeNode.isExpanded());
        }
        return count;
    }
源码


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值