目录树(一)

TreeView

准备

Node

package function.tree;

import com.fr.android.platform.data.bean.IFEntryNode;

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

/**
 * @decs: Node
 * @date: 2018/9/7 11:20
 * @version: v 1.0
 */
public class Node<T> {
    private IFEntryNode ifEntryNode;
    private long id;
    /**
     * 根节点parentId为0
     */
    private long parentId = 0;
    private String name;
    private T item;
    /**
     * 展否
     */
    private boolean isExpand = false;
    private int icon;
    /**
     * 下级子Node
     */
    private List<Node<T>> children = new ArrayList<>();
    /**
     * 父Node
     */
    private Node parent;
    private int resourceId;

    public IFEntryNode getIfEntryNode() {
        return ifEntryNode;
    }

    public void setIfEntryNode(IFEntryNode ifEntryNode) {
        this.ifEntryNode = ifEntryNode;
    }

    public int getIcon() {
        return icon;
    }

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

    public long getId() {
        return id;
    }

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

    public long getParentId() {
        return parentId;
    }

    public void setParentId(long parentId) {
        this.parentId = parentId;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }

    public String getName() {
        return name;
    }

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

    public boolean isExpand() {
        return isExpand;
    }

    public List<Node<T>> getChildren() {
        return children;
    }

    public void setChildren(List<Node<T>> children) {
        this.children = children;
    }

    public Node getParent() {
        return parent;
    }

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

    public int getResourceId() {
        return resourceId;
    }

    public void setResourceId(int resourceId) {
        this.resourceId = resourceId;
    }

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

    /**
     * 父节点展否
     */
    public boolean isParentExpand() {
        return parent != null && parent.isExpand();
    }

    /**
     * 叶子节点否
     */
    public boolean isLeaf() {
        return children.size() == 0;
    }

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

    /**
     * 展
     */
    public void setExpand(boolean isExpand) {
        this.isExpand = isExpand;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Node<?> node = (Node<?>) o;
        return id == node.id &&
                parentId == node.parentId &&
                isExpand == node.isExpand &&
                icon == node.icon &&
                resourceId == node.resourceId &&
                (name != null ? name.equals(node.name) : node.name == null) &&
                (item != null ? item.equals(node.item) : node.item == null) &&
                (children != null ? children.equals(node.children) : node.children == null) &&
                (parent != null ? parent.equals(node.parent) : node.parent == null);
    }

    @Override
    public int hashCode() {
        int result = (int) (id ^ (id >>> 32));
        result = 31 * result + (int) (parentId ^ (parentId >>> 32));
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (item != null ? item.hashCode() : 0);
        result = 31 * result + (isExpand ? 1 : 0);
        result = 31 * result + icon;
        result = 31 * result + (children != null ? children.hashCode() : 0);
        result = 31 * result + (parent != null ? parent.hashCode() : 0);
        result = 31 * result + resourceId;
        return result;
    }
}

Tree

package function.tree;

import com.fr.android.platform.data.bean.IFEntryNode;

/**
 * @decs: Tree
 * @date: 2018/9/7 11:23
 * @version: v 1.0
 */
public interface Tree {
    /**
     * IFEntryNode
     *
     * @return IFEntryNode
     */
    IFEntryNode getIfEntryNode();

    /**
     * ID
     *
     * @return ID
     */
    long getId();

    /**
     * 父ID
     *
     * @return ID
     */
    long getParentId();

    /**
     * 名称
     *
     * @return 名称
     */
    String getName();

    /**
     * 图ID
     *
     * @return 图ID
     */
    int getImageResourceId();
}

TreeAdapter

package function.tree;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.self.zsp.dfs.R;

import java.util.List;

import butterknife.ButterKnife;
import util.DensityUtils;

/**
 * @decs: 多层级树目录(适用RecyclerView)
 * @date: 2018/9/7 11:24
 * @version: v 1.0
 */
public class TreeAdapter<T extends Tree> extends RecyclerView.Adapter<TreeAdapter.TreeViewHolder> {
    private static final float PADDING = 48;
    private Context mContext;
    private List<Node<T>> nodes;
    private TreeItemClickListener treeItemClickListener;

    /**
     * constructor
     *
     * @param context 上下文
     * @param data    数据
     */
    public TreeAdapter(Context context, List<T> data) {
        mContext = context;
        setNodes(data);
    }

    /**
     * 需传数据才刷TreeView(默根节点外节点都折)
     */
    private void setNodes(List<T> data) {
        List<Node<T>> allNodes = TreeHelper.getSortedNode(data, 0);
        nodes = TreeHelper.filterVisibleNode(allNodes);
    }

    public void setListener(TreeItemClickListener mListener) {
        this.treeItemClickListener = mListener;
    }

    /**
     * 指定下标节点源数据
     *
     * @param index 指定下标
     */
    public T getItem(int index) {
        return nodes.get(index).getItem();
    }

    /**
     * 指定id源数据
     *
     * @param id 指定id
     */
    public T getItemById(int id) {
        for (Node<T> node : nodes) {
            if (node.getId() == id) {
                return node.getItem();
            }
        }
        return null;
    }

    /**
     * 添子节点至指定id节点下(强挂数据于指定节点下)
     * 子节点实际父节点并非指定节点则下次调{@link #setNodes(List)}时重调
     *
     * @param index 添数据至该下标节点处
     * @param data  所添数据
     */
    private void addChildrenByIndex(int index, List<T> data) {
        Node<T> parent = nodes.get(index);
        List<Node<T>> childNodes = TreeHelper.getSortedNode(data, 0);
        List<Node<T>> children = parent.getChildren();
        parent.setExpand(true);
        for (Node<T> node : childNodes) {
            node.setParent(parent);
            children.add(node);
        }
        // 展节点
        int count = addChildNodes(parent, index + 1);
        notifyItemChanged(index);
        notifyItemRangeInserted(index + 1, count);
    }

    /**
     * 添子节点至指定id节点下(强挂数据于指定节点下)
     * 子节点实际父节点并非指定节点则下次调{@link #setNodes(List)}时重调
     *
     * @param id   父节点id
     * @param data 所添数据
     */
    public void addChildrenById(int id, List<T> data) {
        int index = findNode(id);
        if (index != -1) {
            addChildrenByIndex(index, data);
        }
    }

    /**
     * 据id查指定节点
     *
     * @param id 所查节点id
     * @return 查找成功的下标(不存返-1)
     */
    private int findNode(int id) {
        int size = nodes.size();
        for (int i = 0; i < size; i++) {
            Node node = nodes.get(i);
            if (node.getId() == id) {
                return i;
            }
        }
        return -1;
    }

    @NonNull
    @Override
    public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View view = inflater.inflate(R.layout.tree_item, parent, false);
        return new TreeViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull TreeAdapter.TreeViewHolder holder, int position) {
        Node node = nodes.get(position);
        holder.setControl(node);
    }

    @Override
    public int getItemCount() {
        return nodes.size();
    }

    class TreeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ImageView ivLeft;
        private TextView tvName;
        private ImageView ivRight;

        private TreeViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
            ivLeft = itemView.findViewById(R.id.ivTreeItemLeft);
            tvName = itemView.findViewById(R.id.tvTreeItemName);
            ivRight = itemView.findViewById(R.id.ivTreeItemRight);
            itemView.setOnClickListener(this);
            ivRight.setOnClickListener(this);
            itemView.setTag(System.currentTimeMillis());
        }

        private void setControl(Node node) {
            tvName.setText(node.getName());
            tvName.setPadding(node.getLevel() * DensityUtils.pxToDp(mContext, PADDING), 0, 0, 0);
            ivRight.setImageResource(node.getResourceId());
            if (node.isLeaf()) {
                ivLeft.setImageResource(0);
                return;
            }
            int rotateDegree = node.isExpand() ? 90 : 0;
            ivLeft.setRotation(0);
            ivLeft.setRotation(rotateDegree);
            ivLeft.setImageResource(R.drawable.arrow_right);
        }

        @Override
        public void onClick(View view) {
            Node<T> node = nodes.get(getLayoutPosition());
            if (view.getId() == R.id.ivTreeItemRight) {
                if (treeItemClickListener != null) {
                    treeItemClickListener.onRightClick(node);
                }
                return;
            }
            if (node != null) {
                if (node.isLeaf()) {
                    if (treeItemClickListener != null) {
                        // Method getTag which get data.
                        treeItemClickListener.onClick(view, node);
                    }
                } else {
                    long lastClickTime = (long) itemView.getTag();
                    // 避快点
                    if (System.currentTimeMillis() - lastClickTime < 200) {
                        return;
                    }
                    itemView.setTag(System.currentTimeMillis());
                    int rotateDegree = node.isExpand() ? -90 : 90;
                    ivLeft.animate().setDuration(100).rotationBy(rotateDegree).start();
                    boolean isExpand = node.isExpand();
                    node.setExpand(!isExpand);
                    if (!isExpand) {
                        notifyItemRangeInserted(getLayoutPosition() + 1, addChildNodes(node, getLayoutPosition() + 1));
                    } else {
                        notifyItemRangeRemoved(getLayoutPosition() + 1, removeChildNodes(node));
                    }
                }
            }
        }
    }

    private int addChildNodes(Node<T> n, int startIndex) {
        List<Node<T>> childList = n.getChildren();
        int addChildCount = 0;
        for (Node<T> node : childList) {
            nodes.add(startIndex + addChildCount++, node);
            // 递归展其下需展子元素
            if (node.isExpand()) {
                addChildCount += addChildNodes(node, startIndex + addChildCount);
            }
        }
        return addChildCount;
    }

    /**
     * 折父节点时删其下全子元素
     *
     * @param n node
     * @return int
     */
    private int removeChildNodes(Node<T> n) {
        if (n.isLeaf()) {
            return 0;
        }
        List<Node<T>> childList = n.getChildren();
        int removeChildCount = childList.size();
        nodes.removeAll(childList);
        for (Node<T> node : childList) {
            if (node.isExpand()) {
                removeChildCount += removeChildNodes(node);
            }
        }
        return removeChildCount;
    }
}

TreeHelper

package function.tree;

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

/**
 * @decs: TreeHelper
 * @date: 2018/9/7 15:38
 * @version: v 1.0
 */
public class TreeHelper {
    /**
     * 传需显数据(转为排序后Node数组)
     *
     * @param data               数据model需继{@link Tree}
     * @param defaultExpandLevel 默展层级
     * @return node数组
     */
    public static <T extends Tree> List<Node<T>> getSortedNode(List<T> data, int defaultExpandLevel) {
        List<Node<T>> result = new ArrayList<>();
        // 转用户数据为List<Node>及设Node间关系(父子)
        List<Node<T>> nodes = convertData2Node(data);
        // 根节点
        List<Node<T>> rootNodes = getRootNodes(nodes);
        // 排序
        for (Node<T> node : rootNodes) {
            addNode(result, node, defaultExpandLevel, 1);
        }
        return result;
    }

    /**
     * 过滤出全可见Node
     */
    public static <T extends Tree> List<Node<T>> filterVisibleNode(List<Node<T>> nodes) {
        List<Node<T>> result = new ArrayList<>();
        for (Node<T> node : nodes) {
            // 根节点或上层目录展状
            if (node.isRoot() || node.isParentExpand()) {
                result.add(node);
            }
        }
        return result;
    }

    private static <T extends Tree> List<Node<T>> convertData2Node(List<T> data) {
        List<Node<T>> list = new ArrayList<>();
        for (T item : data) {
            Node<T> node = new Node<>();
            node.setIfEntryNode(item.getIfEntryNode());
            node.setId(item.getId());
            node.setParentId(item.getParentId());
            node.setName(item.getName());
            node.setResourceId(item.getImageResourceId());
            node.setItem(item);
            list.add(node);
        }
        linkNodes(list);
        return list;
    }

    /**
     * 挂子节点至相应父节点下
     */
    private static <T extends Tree> void linkNodes(List<Node<T>> list) {
        for (int i = 0; i < list.size(); i++) {
            Node<T> n = list.get(i);
            for (int j = i + 1; j < list.size(); j++) {
                Node<T> m = list.get(j);
                if (n.getParentId() == m.getId()) {
                    m.getChildren().add(n);
                    n.setParent(m);
                    continue;
                }
                if (m.getParentId() == n.getId()) {
                    n.getChildren().add(m);
                    m.setParent(n);
                }
            }
        }
    }

    /**
     * 根节点
     *
     * @param nodes 节点集合
     * @param <T>   T
     * @return 根节点
     */
    private static <T extends Tree> List<Node<T>> getRootNodes(List<Node<T>> nodes) {
        List<Node<T>> root = new ArrayList<>();
        for (Node<T> node : nodes) {
            if (node.isRoot()) {
                root.add(node);
            }
        }
        return root;
    }

    /**
     * 添某节点下全子节点
     */
    private static <T extends Tree> void addNode(List<Node<T>> nodes, Node<T> node, int defaultExpandLevel, int currentLevel) {
        nodes.add(node);
        if (defaultExpandLevel == currentLevel) {
            node.setExpand(true);
        }
        if (node.isLeaf()) {
            return;
        }
        for (int i = 0; i < node.getChildren().size(); i++) {
            addNode(nodes, node.getChildren().get(i), defaultExpandLevel, currentLevel + 1);
        }
    }
}

TreeItemClickListener

package function.tree;

import android.view.View;

/**
 * @decs: TreeItemClickListener
 * @date: 2018/9/7 15:52
 * @version: v 1.0
 */
public interface TreeItemClickListener {
    /**
     * 点
     *
     * @param view 视图
     * @param node 节点
     */
    void onClick(View view, Node node);

    /**
     * 右点
     *
     * @param node 节点
     */
    void onRightClick(Node node);
}

tree_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="?actionBarSize"
    android:background="@drawable/white_gray_button_en_r_selector"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/ivTreeItemLeft"
        android:layout_width="@dimen/d48"
        android:layout_height="match_parent"
        android:contentDescription="@string/toDo"
        android:scaleType="centerInside" />

    <TextView
        android:id="@+id/tvTreeItemName"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:textColor="@color/fontInput"
        android:textSize="@dimen/s14"
        tools:text="名称" />

    <ImageView
        android:id="@+id/ivTreeItemRight"
        android:layout_width="@dimen/d48"
        android:layout_height="match_parent"
        android:contentDescription="@string/toDo"
        android:scaleType="centerInside" />
</LinearLayout>

使用

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".drillingdataanalysis.DrillingDataAnalysisActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="?actionBarSize"
        android:background="@color/colorPrimary">

        <ImageView
            android:id="@+id/tvBasicConfigWellTypeBack"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:contentDescription="@string/toDo"
            android:paddingLeft="@dimen/d12"
            android:paddingRight="@dimen/d12"
            app:srcCompat="@drawable/ic_top_back_white" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/drillingDataAnalysis"
            android:textColor="@color/background"
            android:textSize="@dimen/s16" />
    </RelativeLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rvDrillingDataAnalysis"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/background"
        android:scrollbars="none" />
</LinearLayout>

主代码

@BindView(R.id.rvDrillingDataAnalysis)
RecyclerView rvDrillingDataAnalysis;
private List<IfEntryNodeTree> ifEntryNodeTrees;

/*
  false头项/true末项
 */
rvDrillingDataAnalysis.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
// 每item内容不改RecyclerView大小,此设提性能
rvDrillingDataAnalysis.setHasFixedSize(true);
/*
  节点
 */
ifEntryNodeTrees = new ArrayList<>();

for (IFEntryNode ifEntryNode : ifEntryNodes) {
    treeResult(ifEntryNode);
}
display();

/**
 * 树结果
 *
 * @param ifEntryNode 节点
 */
private void treeResult(IFEntryNode ifEntryNode) {
    LogUtils.e(ifEntryNode.getText() + Long.parseLong(ifEntryNode.getId()));
    JsonElement jsonElement = new JsonParser().parse(ifEntryNode.getNodeJSON()).getAsJsonObject().get("parentId");
    long parentId = "0-1".equals(jsonElement.getAsString()) ? 0 : jsonElement.getAsLong();
    ifEntryNodeTrees.add(new IfEntryNodeTree(ifEntryNode, Long.parseLong(ifEntryNode.getId()), parentId, ifEntryNode.getText(), 0));
    if (ifEntryNode.getChildNodes().size() > 0) {
        for (IFEntryNode ifEntryNode1 : ifEntryNode.getChildNodes()) {
            treeResult(ifEntryNode1);
        }
    }
}

/**
 * 展示
 */
private void display() {
    final TreeAdapter<IfEntryNodeTree> adapter = new TreeAdapter<>(this, ifEntryNodeTrees);
    rvDrillingDataAnalysis.setAdapter(adapter);
    // 额外
    final List<IfEntryNodeTree> extra = new ArrayList<>();
    extra.add(new IfEntryNodeTree(null, 0, 1, "测试", 0));
    extra.add(new IfEntryNodeTree(null, 0, 1, "测试", 0));
    extra.add(new IfEntryNodeTree(null, 0, 1, "测试", 0));
    adapter.setListener(new TreeItemClickListener() {
        @Override
        public void onClick(View view, Node node) {
            if (node.isLeaf()) {
                IFIntegrationUtils.loadEntryView(DrillingDataAnalysisActivity.this, node.getIfEntryNode());
            }
        }
       
        @Override
        public void onRightClick(Node node) {
            toastShort(node.getName());
            if (node.getId() == 1) {
                adapter.addChildrenById(1, extra);
            }
        }
    });
}

效果

引申

较少层级用Brvah

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snpmyn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值