效果1
效果2
实现
一步一步可夺城。将自己要实现的东西肢解,那些实现得了的?那些未知的?
思路步骤概要
整个结构分为:树形,节点; 对于Android的结构有:模型(树形,节点),View;
package com.owant.drawtreeview.model;import java.util.LinkedList;/** * Created by owant on 16/12/2016. */public class TreeNode<T> { /*** the parent node,if root node parent node=null;*/ public TreeNode<T> parentNode; /*** the data value*/ public T value; /*** have the child nodes*/ public LinkedList<TreeNode<T>> childNodes; /*** focus tag for the tree add nodes*/ public boolean focus; /*** index of the tree floor*/ public int floor; public TreeNode(T value) {this.value = value;this.childNodes = new LinkedList<TreeNode<T>>();//this.focus = false;//this.parentNode = null; } public TreeNode<T> getParentNode() {return parentNode; } public void setParentNode(TreeNode<T> parentNode) {this.parentNode = parentNode; } public T getValue() {return value; } public void setValue(T value) {this.value = value; } public LinkedList<TreeNode<T>> getChildNodes() {return childNodes; } public void setChildNodes(LinkedList<TreeNode<T>> childNodes) {this.childNodes = childNodes; } public boolean isFocus() {return focus; } public void setFocus(boolean focus) {this.focus = focus; } public int getFloor() {return floor; } public void setFloor(int floor) {this.floor = floor; }}2.树形。根节点,添加节点,遍历,上一个节点,下一个节点,基于点拆分的上下节点集合。
package com.owant.drawtreeview.model;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.Deque;import java.util.LinkedList;import java.util.Stack;/** * Created by owant on 16/12/2016. */public class Tree<T> { /*** the root for the tree*/ public TreeNode<T> rootNode; public Tree(TreeNode<T> rootNode) {this.rootNode = rootNode; } /*** add the node in some father node** @param start* @param nodes*/ public void addNode(TreeNode<T> start, TreeNode<T>... nodes) {int index = 1;TreeNode<T> temp = start;if (temp.getParentNode() != null) { index = temp.getParentNode().floor;}for (TreeNode<T> t : nodes) { t.setParentNode(start); t.setFloor(index); start.getChildNodes().add(t);} } public boolean remvoeNode(TreeNode<T> starNode, TreeNode<T> deleteNote) {boolean rm = false;int size = starNode.getChildNodes().size();if (size > 0) { rm = starNode.getChildNodes().remove(deleteNote);}return rm; } public TreeNode<T> getRootNode() {return rootNode; } public void setRootNode(TreeNode<T> rootNode) {this.rootNode = rootNode; } /*** 同一个父节点的上下** @param midPreNode* @return* @throws NotFindNodeException*/ public TreeNode<T> getLowNode(TreeNode<T> midPreNode) {TreeNode<T> find = null;TreeNode<T> parentNode = midPreNode.getParentNode();if (parentNode != null && parentNode.getChildNodes().size() >= 2) { Deque<TreeNode<T>> queue = new ArrayDeque<>(); TreeNode<T> rootNode = parentNode; queue.add(rootNode); boolean up = false; while (!queue.isEmpty()) {rootNode = (TreeNode<T>) queue.poll();if (up) { if (rootNode.getFloor() == midPreNode.getFloor()) {find = rootNode; } break;}//到了该元素if (rootNode == midPreNode) up = true;LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes();if (childNodes.size() > 0) { for (TreeNode<T> item : childNodes) {queue.add(item); }} }}return find; } public TreeNode<T> getPreNode(TreeNode<T> midPreNode) {TreeNode<T> parentNode = midPreNode.getParentNode();TreeNode<T> find = null;if (parentNode != null && parentNode.getChildNodes().size() > 0) { Deque<TreeNode<T>> queue = new ArrayDeque<>(); TreeNode<T> rootNode = parentNode; queue.add(rootNode); while (!queue.isEmpty()) {rootNode = (TreeNode<T>) queue.poll();//到了该元素if (rootNode == midPreNode) { //返回之前的值 break;}find = rootNode;LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes();if (childNodes.size() > 0) { for (TreeNode<T> item : childNodes) {queue.add(item); }} } if (find != null && find.getFloor() != midPreNode.getFloor()) {find = null; }}return find; } public ArrayList<TreeNode<T>> getAllLowNodes(TreeNode<T> addNode) {ArrayList<TreeNode<T>> array = new ArrayList<>();TreeNode<T> parentNode = addNode.getParentNode();while (parentNode != null) { TreeNode<T> lowNode = getLowNode(parentNode); while (lowNode != null) {array.add(lowNode);lowNode = getLowNode(lowNode); } parentNode = parentNode.getParentNode();}return array; } public ArrayList<TreeNode<T>> getAllPreNodes(TreeNode<T> addNode) {ArrayList<TreeNode<T>> array = new ArrayList<>();TreeNode<T> parentNode = addNode.getParentNode();while (parentNode != null) { TreeNode<T> lowNode = getPreNode(parentNode); while (lowNode != null) {array.add(lowNode);lowNode = getPreNode(lowNode); } parentNode = parentNode.getParentNode();}return array; } public LinkedList<TreeNode<T>> getNodeChildNodes(TreeNode<T> node) {return node.getChildNodes(); } public void printTree() {Stack<TreeNode<T>> stack = new Stack<>();TreeNode<T> rootNode = getRootNode();stack.add(rootNode);while (!stack.isEmpty()) { TreeNode<T> pop = stack.pop(); System.out.println(pop.getValue().toString()); LinkedList<TreeNode<T>> childNodes = pop.getChildNodes(); for (TreeNode<T> item : childNodes) {stack.add(item); }} } public void printTree2() {Deque<TreeNode<T>> queue = new ArrayDeque<>();TreeNode<T> rootNode = getRootNode();queue.add(rootNode);while (!queue.isEmpty()) { rootNode = (TreeNode<T>) queue.poll(); System.out.println(rootNode.getValue().toString()); LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes(); if (childNodes.size() > 0) {for (TreeNode<T> item : childNodes) { queue.add(item);} }} }}3.测试模型 当我们实现了模型后,要写一些列子来测试模型是否正确,进行打印,遍历等测试,这是很重要的。对于树形的node的上一个node和下一个node的理解等。
package com.owant.drawtreeview.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.view.ViewGroup;import com.owant.drawtreeview.R;import com.owant.drawtreeview.model.Tree;import com.owant.drawtreeview.model.TreeNode;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.Deque;import java.util.LinkedList;/** * Created by owant on 09/01/2017. */public class SuperTreeView extends ViewGroup {/** * the default x,y mDx */private int mDx;private int mDy;private int mWith;private int mHeight;private Context mContext;private Tree<String> mTree;private ArrayList<NodeView> mNodesViews;public SuperTreeView(Context context) {this(context, null, 0);}public SuperTreeView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SuperTreeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;mNodesViews = new ArrayList<>();mContext = context;mDx = dp2px(mContext, 26);mDy = dp2px(mContext, 22);}/** * 添加view到Group */private void onAddNodeViews() {if (mTree != null) {TreeNode<String> rootNode = mTree.getRootNode();Deque<TreeNode<String>> deque = new ArrayDeque<>();deque.add(rootNode);while (!deque.isEmpty()) {TreeNode<String> poll = deque.poll();NodeView nodeView = new NodeView(mContext);nodeView.setTreeNode(poll);ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);nodeView.setLayoutParams(lp);this.addView(nodeView);mNodesViews.add(nodeView);LinkedList<TreeNode<String>> childNodes = poll.getChildNodes();for (TreeNode<String> ch : childNodes) {deque.push(ch);}}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);final int size = getChildCount();for (int i = 0; i < size; i++) {measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mHeight = getMeasuredHeight();mWith = getMeasuredWidth();if (mTree != null) {NodeView rootView = findTreeNodeView(mTree.getRootNode());if (rootView != null) {//root的位置rootTreeViewLayout(rootView);//标准位置for (NodeView nv : mNodesViews) {standardTreeChildLayout(nv);}//基于父子的移动for (NodeView nv : mNodesViews) {fatherChildCorrect(nv);}}}}private void rootTreeViewLayout(NodeView rootView) {int lr = mDy;int tr = mHeight / 2 - rootView.getMeasuredHeight() / 2;int rr = lr + rootView.getMeasuredWidth();int br = tr + rootView.getMeasuredHeight();rootView.layout(lr, tr, rr, br);}@Overrideprotected void dispatchDraw(Canvas canvas) {if (mTree != null) {drawTreeLine(canvas, mTree.getRootNode());}super.dispatchDraw(canvas);}/** * 标准的位置分布 * * @param rootView */private void standardTreeChildLayout(NodeView rootView) {TreeNode<String> treeNode = rootView.getTreeNode();if (treeNode != null) {//所有的子节点LinkedList<TreeNode<String>> childNodes = treeNode.getChildNodes();int size = childNodes.size();int mid = size / 2;int r = size % 2;//基线//b//a-------//c//int left = rootView.getRight() + mDx;int top = rootView.getTop() + rootView.getMeasuredHeight() / 2;int right = 0;int bottom = 0;if (size == 0) {return;} else if (size == 1) {NodeView midChildNodeView = findTreeNodeView(childNodes.get(0));top = top - midChildNodeView.getMeasuredHeight() / 2;right = left + midChildNodeView.getMeasuredWidth();bottom = top + midChildNodeView.getMeasuredHeight();midChildNodeView.layout(left, top, right, bottom);} else {int topLeft = left;int topTop = top;int topRight = 0;int topBottom = 0;int bottomLeft = left;int bottomTop = top;int bottomRight = 0;int bottomBottom = 0;if (r == 0) {//偶数for (int i = mid - 1; i >= 0; i--) {NodeView topView = findTreeNodeView(childNodes.get(i));NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));if (i == mid - 1) {topTop = topTop - mDy / 2 - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy / 2;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();} else {topTop = topTop - mDy - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();}topView.layout(topLeft, topTop, topRight, topBottom);bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);bottomTop = bottomView.getBottom();}} else {NodeView midView = findTreeNodeView(childNodes.get(mid));midView.layout(left, top - midView.getMeasuredHeight() / 2, left + midView.getMeasuredWidth(),top - midView.getMeasuredHeight() / 2 + midView.getMeasuredHeight());topTop = midView.getTop();bottomTop = midView.getBottom();for (int i = mid - 1; i >= 0; i--) {NodeView topView = findTreeNodeView(childNodes.get(i));NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));topTop = topTop - mDy - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();topView.layout(topLeft, topTop, topRight, topBottom);bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);bottomTop = bottomView.getBottom();}}}}}/** * 移动 * * @param rootView * @param dy */private void moveNodeLayout(NodeView rootView, int dy) {Deque<TreeNode<String>> queue = new ArrayDeque<>();TreeNode<String> rootNode = rootView.getTreeNode();queue.add(rootNode);while (!queue.isEmpty()) {rootNode = (TreeNode<String>) queue.poll();rootView = findTreeNodeView(rootNode);int l = rootView.getLeft();int t = rootView.getTop() + dy;rootView.layout(l, t, l + rootView.getMeasuredWidth(), t + rootView.getMeasuredHeight());LinkedList<TreeNode<String>> childNodes = rootNode.getChildNodes();for (TreeNode<String> item : childNodes) {queue.add(item);}}}private void fatherChildCorrect(NodeView nv) {int count = nv.getTreeNode().getChildNodes().size();if (nv.getParent() != null && count >= 2) {TreeNode<String> tn = nv.getTreeNode().getChildNodes().get(0);TreeNode<String> bn = nv.getTreeNode().getChildNodes().get(count - 1);Log.i("see fc", nv.getTreeNode().getValue() + ":" + tn.getValue() + "," + bn.getValue());int topDr = nv.getTop() - findTreeNodeView(tn).getBottom() + mDy;int bnDr = findTreeNodeView(bn).getTop() - nv.getBottom() + mDy;//上移动ArrayList<TreeNode<String>> allLowNodes = mTree.getAllLowNodes(bn);ArrayList<TreeNode<String>> allPreNodes = mTree.getAllPreNodes(tn);for (TreeNode<String> low : allLowNodes) {NodeView view = findTreeNodeView(low);moveNodeLayout(view, bnDr);}for (TreeNode<String> pre : allPreNodes) {NodeView view = findTreeNodeView(pre);moveNodeLayout(view, -topDr);}}}/** * 绘制树形的连线 * * @param canvas * @param root */private void drawTreeLine(Canvas canvas, TreeNode<String> root) {NodeView fatherView = findTreeNodeView(root);if (fatherView != null) {LinkedList<TreeNode<String>> childNodes = root.getChildNodes();for (TreeNode<String> node : childNodes) {drawLineToView(canvas, fatherView, findTreeNodeView(node));drawTreeLine(canvas, node);}}}/** * 绘制两个View直接的连线 * * @param canvas * @param from * @param to */private void drawLineToView(Canvas canvas, View from, View to) {Paint paint = new Paint();paint.setAntiAlias(true);paint.setStyle(Paint.Style.STROKE);float width = 2f;paint.setStrokeWidth(dp2px(mContext, width));paint.setColor(mContext.getResources().getColor(R.color.chelsea_cucumber));int top = from.getTop();int formY = top + from.getMeasuredHeight() / 2;int formX = from.getRight();int top1 = to.getTop();int toY = top1 + to.getMeasuredHeight() / 2;int toX = to.getLeft();Path path = new Path();path.moveTo(formX, formY);path.quadTo(toX - dp2px(mContext, 15), toY, toX, toY);canvas.drawPath(path, paint);}private NodeView findTreeNodeView(TreeNode<String> node) {NodeView v = null;for (NodeView view : mNodesViews) {if (view.getTreeNode() == node) {v = view;continue;}}return v;}public int dp2px(Context context, float dpVal) {int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());return result;}public void setTree(Tree<String> tree) {this.mTree = tree;onAddNodeViews();}public Tree<String> getTree() {return mTree;}}粘贴代码后发现,没有什么好说了,对于读者来说,应该是一脸懵逼的。毕竟,那个位置是如何确定的呀,view和view的连线呀…… 对于整个View来说这是最难的,我也是探索了好久才得出结论的。首先,对于一个只有两层的树形来说,如图:
/** * 标准的位置分布 * * @param rootView */private void standardTreeChildLayout(NodeView rootView) {TreeNode<String> treeNode = rootView.getTreeNode();if (treeNode != null) {//所有的子节点LinkedList<TreeNode<String>> childNodes = treeNode.getChildNodes();int size = childNodes.size();int mid = size / 2;int r = size % 2;//基线//b//a-------//c//int left = rootView.getRight() + mDx;int top = rootView.getTop() + rootView.getMeasuredHeight() / 2;int right = 0;int bottom = 0;if (size == 0) {return;} else if (size == 1) {NodeView midChildNodeView = findTreeNodeView(childNodes.get(0));top = top - midChildNodeView.getMeasuredHeight() / 2;right = left + midChildNodeView.getMeasuredWidth();bottom = top + midChildNodeView.getMeasuredHeight();midChildNodeView.layout(left, top, right, bottom);} else {int topLeft = left;int topTop = top;int topRight = 0;int topBottom = 0;int bottomLeft = left;int bottomTop = top;int bottomRight = 0;int bottomBottom = 0;if (r == 0) {//偶数for (int i = mid - 1; i >= 0; i--) {NodeView topView = findTreeNodeView(childNodes.get(i));NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));if (i == mid - 1) {topTop = topTop - mDy / 2 - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy / 2;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();} else {topTop = topTop - mDy - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();}topView.layout(topLeft, topTop, topRight, topBottom);bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);bottomTop = bottomView.getBottom();}} else {NodeView midView = findTreeNodeView(childNodes.get(mid));midView.layout(left, top - midView.getMeasuredHeight() / 2, left + midView.getMeasuredWidth(),top - midView.getMeasuredHeight() / 2 + midView.getMeasuredHeight());topTop = midView.getTop();bottomTop = midView.getBottom();for (int i = mid - 1; i >= 0; i--) {NodeView topView = findTreeNodeView(childNodes.get(i));NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));topTop = topTop - mDy - topView.getMeasuredHeight();topRight = topLeft + topView.getMeasuredWidth();topBottom = topTop + topView.getMeasuredHeight();bottomTop = bottomTop + mDy;bottomRight = bottomLeft + bottomView.getMeasuredWidth();bottomBottom = bottomTop + bottomView.getMeasuredHeight();topView.layout(topLeft, topTop, topRight, topBottom);bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);bottomTop = bottomView.getBottom();}}}}}之后等到的View情况如下:
说明我们还需要纠正。下面是纠正的探索,我精简一下结构如下情况:
发现:
B需要在E的上面;
D需要在I的下面;
就是EI要撑开ID的位置。之后我们可以先写这个算法,发现基本可以了。但是还是有问题,同层的还是会重合,只有我们又进行同层的纠正。发现好像解决了,其实还是不行,测试还是发现问题,对于单伸长还有问题,之后又是修改…………
最后发现……这里就不说了,就是自己要探索啦。
总结
最后我才发现了那个完善的纠正算法,就是代码了的。大家可以在我的github中找到我之前的TreeView中的那个位置算法探索。欢迎大家支持:https://github.com/owant/TreeView
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。