Android樹形控件繪制方法
前言
作為一個(gè)開發(fā)者,日常會(huì)接觸到很多優(yōu)秀的軟件,其實(shí),或多或少會(huì)有這樣的想法,我能不能開發(fā)一個(gè)自己軟件,甚至辦公軟件都希望是Markdown的文本,為何用office?我常常想自己做一個(gè)IDE什么的。但是,很多只是想了一下就過(guò)了,一直沒(méi)有實(shí)現(xiàn).
我接觸思維導(dǎo)圖軟件已經(jīng)很久的了,開始是使用微軟的思維導(dǎo)圖軟件,接著XMind,后來(lái)使用了MindMaple Lite。感覺很好用的。也想過(guò)如何去實(shí)現(xiàn)一個(gè)思維導(dǎo)圖的軟件,加之我特別注意軟件的快捷鍵,我選取軟件常常是,看快捷如何,快捷鍵差的就不要了?;谧约旱膶?shí)踐使用思維導(dǎo)圖。前一個(gè)月我就在github上實(shí)現(xiàn)了一個(gè)樹形圖的Android控件,這個(gè)其實(shí)是我想實(shí)現(xiàn)思維導(dǎo)圖的開始。實(shí)現(xiàn)后,才發(fā)現(xiàn)并沒(méi)有多大的障礙。下面我就說(shuō)說(shuō)我如何打造一個(gè)樹形控件的。先上效果:

效果1

效果2
實(shí)現(xiàn)
一步一步可奪城。將自己要實(shí)現(xiàn)的東西肢解,那些實(shí)現(xiàn)得了的?那些未知的?
思路步驟概要
整個(gè)結(jié)構(gòu)分為:樹形,節(jié)點(diǎn); 對(duì)于Android的結(jié)構(gòu)有:模型(樹形,節(jié)點(diǎn)),View;
- 實(shí)現(xiàn)樹形的節(jié)點(diǎn)node 的Model;
- 實(shí)現(xiàn)樹形Model;
- 實(shí)現(xiàn)View的繪制:1.添加View;2.確定Nodes的位置;3.連線Node;
詳細(xì)步驟
看到思路步驟概要后,相信我們的思路已經(jīng)很清晰了。感覺是不是很simple,是的,實(shí)現(xiàn)也如此。到這里了,我就開始編碼。但是為了教會(huì)大家,我提幾個(gè)疑問(wèn)給大家:
樹的遍歷如何實(shí)現(xiàn)?(可以google,如果你學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)當(dāng)然simple了)
節(jié)點(diǎn)和節(jié)點(diǎn)這間使用什么關(guān)聯(lián)?(next)
如何確定Node的位置?位置有什么規(guī)律?(??)
如何實(shí)現(xiàn)兩個(gè)View之間的連線?(??)
……
其實(shí)問(wèn)題還真的有一點(diǎn)。但是這些都不能妨礙我們的步伐,還是寫好已知的代碼吧 。
代碼
1.樹的節(jié)點(diǎn)。主要是一些需要的數(shù)據(jù)。父節(jié)點(diǎn),值,子節(jié)點(diǎn),是否對(duì)焦(對(duì)于將來(lái)用的),在樹形的層……
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.樹形。根節(jié)點(diǎn),添加節(jié)點(diǎn),遍歷,上一個(gè)節(jié)點(diǎn),下一個(gè)節(jié)點(diǎn),基于點(diǎn)拆分的上下節(jié)點(diǎn)集合。
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;
}
/**
* 同一個(gè)父節(jié)點(diǎn)的上下
*
* @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.測(cè)試模型 當(dāng)我們實(shí)現(xiàn)了模型后,要寫一些列子來(lái)測(cè)試模型是否正確,進(jìn)行打印,遍歷等測(cè)試,這是很重要的。對(duì)于樹形的node的上一個(gè)node和下一個(gè)node的理解等。

4.樹形的View
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);
}
}
}
}
@Override
protected 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);
}
}
@Override
protected 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);
//標(biāo)準(zhǔn)位置
for (NodeView nv : mNodesViews) {
standardTreeChildLayout(nv);
}
//基于父子的移動(dòng)
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);
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mTree != null) {
drawTreeLine(canvas, mTree.getRootNode());
}
super.dispatchDraw(canvas);
}
/**
* 標(biāo)準(zhǔn)的位置分布
*
* @param rootView
*/
private void standardTreeChildLayout(NodeView rootView) {
TreeNode<String> treeNode = rootView.getTreeNode();
if (treeNode != null) {
//所有的子節(jié)點(diǎn)
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) {//偶數(shù)
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();
}
}
}
}
}
/**
* 移動(dòng)
*
* @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;
//上移動(dòng)
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);
}
}
}
/**
* 繪制兩個(gè)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;
}
}
粘貼代碼后發(fā)現(xiàn),沒(méi)有什么好說(shuō)了,對(duì)于讀者來(lái)說(shuō),應(yīng)該是一臉懵逼的。畢竟,那個(gè)位置是如何確定的呀,view和view的連線呀…… 對(duì)于整個(gè)View來(lái)說(shuō)這是最難的,我也是探索了好久才得出結(jié)論的。首先,對(duì)于一個(gè)只有兩層的樹形來(lái)說(shuō),如圖:

PQRS是基于F來(lái)計(jì)算的,之后分布。之后我就得到的方法如下:
/**
* 標(biāo)準(zhǔn)的位置分布
*
* @param rootView
*/
private void standardTreeChildLayout(NodeView rootView) {
TreeNode<String> treeNode = rootView.getTreeNode();
if (treeNode != null) {
//所有的子節(jié)點(diǎn)
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) {//偶數(shù)
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情況如下:

說(shuō)明我們還需要糾正。下面是糾正的探索,我精簡(jiǎn)一下結(jié)構(gòu)如下情況:

發(fā)現(xiàn):
B需要在E的上面;
D需要在I的下面;
就是EI要撐開ID的位置。之后我們可以先寫這個(gè)算法,發(fā)現(xiàn)基本可以了。但是還是有問(wèn)題,同層的還是會(huì)重合,只有我們又進(jìn)行同層的糾正。發(fā)現(xiàn)好像解決了,其實(shí)還是不行,測(cè)試還是發(fā)現(xiàn)問(wèn)題,對(duì)于單伸長(zhǎng)還有問(wèn)題,之后又是修改…………
最后發(fā)現(xiàn)……這里就不說(shuō)了,就是自己要探索啦。
總結(jié)
最后我才發(fā)現(xiàn)了那個(gè)完善的糾正算法,就是代碼了的。大家可以在我的github中找到我之前的TreeView中的那個(gè)位置算法探索。歡迎大家支持:https://github.com/owant/TreeView
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android使用Canvas繪制圓形進(jìn)度條效果
- Android編程開發(fā)之在Canvas中利用Path繪制基本圖形(圓形,矩形,橢圓,三角形等)
- Android編程之canvas繪制各種圖形(點(diǎn),直線,弧,圓,橢圓,文字,矩形,多邊形,曲線,圓角矩形)
- Android中使用Canvas繪制南丁格爾玫瑰圖(Nightingale rose diagram)
- Android canvas畫圖操作之切割畫布實(shí)現(xiàn)方法(clipRect)
- Android開發(fā)之圖形圖像與動(dòng)畫(一)Paint和Canvas類學(xué)習(xí)
- Android自定義控件繪制基本圖形基礎(chǔ)入門
- Android自定義view繪制圓環(huán)占比動(dòng)畫
- android繪制圓形圖片的兩種方式示例
- Android自定義View實(shí)現(xiàn)shape圖形繪制
- Android編程實(shí)現(xiàn)canvas繪制餅狀統(tǒng)計(jì)圖功能示例【自動(dòng)適應(yīng)條目數(shù)量與大小】
相關(guān)文章
Android實(shí)現(xiàn)點(diǎn)擊切換視圖并跳轉(zhuǎn)傳值
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)點(diǎn)擊切換視圖并跳轉(zhuǎn)傳值,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android實(shí)現(xiàn)USB掃碼槍獲取掃描內(nèi)容
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)USB掃碼槍獲取掃描內(nèi)容,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
php使用phpmailer發(fā)送郵件實(shí)例解析
這篇文章主要為大家詳細(xì)解析了php使用phpmailer發(fā)送郵件實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07
基于Android實(shí)現(xiàn)跳轉(zhuǎn)到WiFi開關(guān)設(shè)置頁(yè)的詳細(xì)步驟
在Android應(yīng)用開發(fā)中,有時(shí)候需要引導(dǎo)用戶到特定的系統(tǒng)設(shè)置頁(yè)面,例如Wi-Fi開關(guān)設(shè)置頁(yè),可以通過(guò)隱式Intent來(lái)實(shí)現(xiàn)這一功能,以下是詳細(xì)的步驟以及相關(guān)的Kotlin代碼示例,需要的朋友可以參考下2024-09-09
發(fā)布?Android?library?到?Maven?解析
這篇文章主要介紹了發(fā)布?Android?library到Maven解析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
Android實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06

