前言

作为一个开发者,日常会接触到很多优秀的软件,其实,或多或少会有这样的想法,我能不能开发一个自己软件,甚至办公软件都希望是Markdown的文本,为何用office?我常常想自己做一个IDE什么的。但是,很多只是想了一下就过了,一直没有实现.

我接触思维导图软件已经很久的了,开始是使用微软的思维导图软件,接着XMind,后来使用了MindMaple Lite。感觉很好用的。也想过如何去实现一个思维导图的软件,加之我特别注意软件的快捷键,我选取软件常常是,看快捷如何,快捷键差的就不要了。基于自己的实践使用思维导图。前一个月我就在github上实现了一个树形图的Android控件,这个其实是我想实现思维导图的开始。实现后,才发现并没有多大的障碍。下面我就说说我如何打造一个树形控件的。先上效果:

效果1

效果2

实现

一步一步可夺城。将自己要实现的东西肢解,那些实现得了的?那些未知的?

思路步骤概要

整个结构分为:树形,节点; 对于Android的结构有:模型(树形,节点),View;

实现树形的节点node 的Model;

实现树形Model;

实现View的绘制:1.添加View;2.确定Nodes的位置;3.连线Node;

详细步骤

看到思路步骤概要后,相信我们的思路已经很清晰了。感觉是不是很simple,是的,实现也如此。到这里了,我就开始编码。但是为了教会大家,我提几个疑问给大家:

树的遍历如何实现?(可以google,如果你学过数据结构当然simple了)

节点和节点这间使用什么关联?(next)

如何确定Node的位置?位置有什么规律?(??)

如何实现两个View之间的连线?(??)

……

其实问题还真的有一点。但是这些都不能妨碍我们的步伐,还是写好已知的代码吧 。

代码

1.树的节点。主要是一些需要的数据。父节点,值,子节点,是否对焦(对于将来用的),在树形的层……

package com.owant.drawtreeview.model;

import java.util.LinkedList;

/**

* Created by owant on 16/12/2016.

*/

public class TreeNode {

/**

* the parent node,if root node parent node=null;

*/

public TreeNode parentNode;

/**

* the data value

*/

public T value;

/**

* have the child nodes

*/

public LinkedList> 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>();

// this.focus = false;

// this.parentNode = null;

}

public TreeNode getParentNode() {

return parentNode;

}

public void setParentNode(TreeNode parentNode) {

this.parentNode = parentNode;

}

public T getValue() {

return value;

}

public void setValue(T value) {

this.value = value;

}

public LinkedList> getChildNodes() {

return childNodes;

}

public void setChildNodes(LinkedList> 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 {

/**

* the root for the tree

*/

public TreeNode rootNode;

public Tree(TreeNode rootNode) {

this.rootNode = rootNode;

}

/**

* add the node in some father node

*

* @param start

* @param nodes

*/

public void addNode(TreeNode start, TreeNode... nodes) {

int index = 1;

TreeNode temp = start;

if (temp.getParentNode() != null) {

index = temp.getParentNode().floor;

}

for (TreeNode t : nodes) {

t.setParentNode(start);

t.setFloor(index);

start.getChildNodes().add(t);

}

}

public boolean remvoeNode(TreeNode starNode, TreeNode deleteNote) {

boolean rm = false;

int size = starNode.getChildNodes().size();

if (size > 0) {

rm = starNode.getChildNodes().remove(deleteNote);

}

return rm;

}

public TreeNode getRootNode() {

return rootNode;

}

public void setRootNode(TreeNode rootNode) {

this.rootNode = rootNode;

}

/**

* 同一个父节点的上下

*

* @param midPreNode

* @return

* @throws NotFindNodeException

*/

public TreeNode getLowNode(TreeNode midPreNode) {

TreeNode find = null;

TreeNode parentNode = midPreNode.getParentNode();

if (parentNode != null && parentNode.getChildNodes().size() >= 2) {

Deque> queue = new ArrayDeque<>();

TreeNode rootNode = parentNode;

queue.add(rootNode);

boolean up = false;

while (!queue.isEmpty()) {

rootNode = (TreeNode) queue.poll();

if (up) {

if (rootNode.getFloor() == midPreNode.getFloor()) {

find = rootNode;

}

break;

}

//到了该元素

if (rootNode == midPreNode) up = true;

LinkedList> childNodes = rootNode.getChildNodes();

if (childNodes.size() > 0) {

for (TreeNode item : childNodes) {

queue.add(item);

}

}

}

}

return find;

}

public TreeNode getPreNode(TreeNode midPreNode) {

TreeNode parentNode = midPreNode.getParentNode();

TreeNode find = null;

if (parentNode != null && parentNode.getChildNodes().size() > 0) {

Deque> queue = new ArrayDeque<>();

TreeNode rootNode = parentNode;

queue.add(rootNode);

while (!queue.isEmpty()) {

rootNode = (TreeNode) queue.poll();

//到了该元素

if (rootNode == midPreNode) {

//返回之前的值

break;

}

find = rootNode;

LinkedList> childNodes = rootNode.getChildNodes();

if (childNodes.size() > 0) {

for (TreeNode item : childNodes) {

queue.add(item);

}

}

}

if (find != null && find.getFloor() != midPreNode.getFloor()) {

find = null;

}

}

return find;

}

public ArrayList> getAllLowNodes(TreeNode addNode) {

ArrayList> array = new ArrayList<>();

TreeNode parentNode = addNode.getParentNode();

while (parentNode != null) {

TreeNode lowNode = getLowNode(parentNode);

while (lowNode != null) {

array.add(lowNode);

lowNode = getLowNode(lowNode);

}

parentNode = parentNode.getParentNode();

}

return array;

}

public ArrayList> getAllPreNodes(TreeNode addNode) {

ArrayList> array = new ArrayList<>();

TreeNode parentNode = addNode.getParentNode();

while (parentNode != null) {

TreeNode lowNode = getPreNode(parentNode);

while (lowNode != null) {

array.add(lowNode);

lowNode = getPreNode(lowNode);

}

parentNode = parentNode.getParentNode();

}

return array;

}

public LinkedList> getNodeChildNodes(TreeNode node) {

return node.getChildNodes();

}

public void printTree() {

Stack> stack = new Stack<>();

TreeNode rootNode = getRootNode();

stack.add(rootNode);

while (!stack.isEmpty()) {

TreeNode pop = stack.pop();

System.out.println(pop.getValue().toString());

LinkedList> childNodes = pop.getChildNodes();

for (TreeNode item : childNodes) {

stack.add(item);

}

}

}

public void printTree2() {

Deque> queue = new ArrayDeque<>();

TreeNode rootNode = getRootNode();

queue.add(rootNode);

while (!queue.isEmpty()) {

rootNode = (TreeNode) queue.poll();

System.out.println(rootNode.getValue().toString());

LinkedList> childNodes = rootNode.getChildNodes();

if (childNodes.size() > 0) {

for (TreeNode item : childNodes) {

queue.add(item);

}

}

}

}

}

3.测试模型当我们实现了模型后,要写一些列子来测试模型是否正确,进行打印,遍历等测试,这是很重要的。对于树形的node的上一个node和下一个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 mTree;

private ArrayList 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 rootNode = mTree.getRootNode();

Deque> deque = new ArrayDeque<>();

deque.add(rootNode);

while (!deque.isEmpty()) {

TreeNode 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> childNodes = poll.getChildNodes();

for (TreeNode 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);

//标准位置

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);

}

@Override

protected void dispatchDraw(Canvas canvas) {

if (mTree != null) {

drawTreeLine(canvas, mTree.getRootNode());

}

super.dispatchDraw(canvas);

}

/**

* 标准的位置分布

*

* @param rootView

*/

private void standardTreeChildLayout(NodeView rootView) {

TreeNode treeNode = rootView.getTreeNode();

if (treeNode != null) {

//所有的子节点

LinkedList> 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> queue = new ArrayDeque<>();

TreeNode rootNode = rootView.getTreeNode();

queue.add(rootNode);

while (!queue.isEmpty()) {

rootNode = (TreeNode) 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> childNodes = rootNode.getChildNodes();

for (TreeNode item : childNodes) {

queue.add(item);

}

}

}

private void fatherChildCorrect(NodeView nv) {

int count = nv.getTreeNode().getChildNodes().size();

if (nv.getParent() != null && count >= 2) {

TreeNode tn = nv.getTreeNode().getChildNodes().get(0);

TreeNode 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> allLowNodes = mTree.getAllLowNodes(bn);

ArrayList> allPreNodes = mTree.getAllPreNodes(tn);

for (TreeNode low : allLowNodes) {

NodeView view = findTreeNodeView(low);

moveNodeLayout(view, bnDr);

}

for (TreeNode pre : allPreNodes) {

NodeView view = findTreeNodeView(pre);

moveNodeLayout(view, -topDr);

}

}

}

/**

* 绘制树形的连线

*

* @param canvas

* @param root

*/

private void drawTreeLine(Canvas canvas, TreeNode root) {

NodeView fatherView = findTreeNodeView(root);

if (fatherView != null) {

LinkedList> childNodes = root.getChildNodes();

for (TreeNode 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 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 tree) {

this.mTree = tree;

onAddNodeViews();

}

public Tree getTree() {

return mTree;

}

}

粘贴代码后发现,没有什么好说了,对于读者来说,应该是一脸懵逼的。毕竟,那个位置是如何确定的呀,view和view的连线呀…… 对于整个View来说这是最难的,我也是探索了好久才得出结论的。首先,对于一个只有两层的树形来说,如图:

PQRS是基于F来计算的,之后分布。之后我就得到的方法如下:

/**

* 标准的位置分布

*

* @param rootView

*/

private void standardTreeChildLayout(NodeView rootView) {

TreeNode treeNode = rootView.getTreeNode();

if (treeNode != null) {

//所有的子节点

LinkedList> 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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java生成树型思维导图,Android树形控件绘制方法相关推荐

  1. Android课程思维导图,Android实现思维导图

    最近,小弟在实现一个思维导图的开源控件.下面我简单介绍一下如下打造一个类似思维导图软件的ViewGroup. 基本结构.png 建立模型 主要模型结构相对简单:TreeModel,NoteModel, ...

  2. 共享思维导图,协作型思维导图Leangoo

    共享思维导图,协作型思维导图Leangoo,可以导出导入XMind文件了,我们来看看如何操作: 进入leangoo官网:www.leangoo.com,登陆账号. 点击右上角"+" ...

  3. Java语言学习思维导图

    Java语言学习思维导图

  4. Java最全思维导图知识汇总

    本篇主要总结java知识思维导图框架,总结内容从基础到高级,到Java开发.可供Java学习者学习. Java知识总结: 01.Java程序设计(基础) 02.Java程序设计(专题) 03.客户端网 ...

  5. Java基础知识思维导图

    Java基础知识思维导图 涉及基础语法.面向对象.数据结构.高级特性.异常处理.多线程.常用工具类.常用设计模式等. 初学者可参考.

  6. Java知识汇总——思维导图

    Java知识汇总--思维导图 Java知识点汇总,从基础到常用的API.还有常用的集合类,总结的很详细.图片是从论坛里面找到的,整理下来以便常复习.这是目前看到的比较详细完整的思维导图. 1.Java ...

  7. java集合框架思维导图

    一.JAVA集合框架思维导图 二.java集合框架图 三.Java集合框架体系图

  8. Java基础学习—思维导图

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/52894625冷血之心的博客) 找到两张Java学习的思维导图,特别 ...

  9. Java并发编程思维导图(知识点总结,含面试题整理)

    我公布的所有思维导图笔记(后端技术知识点汇总)  目录链接 前言 继JVM思维导图之后又一肝作 年前刚好整理完毕,公开克隆分享. 本张思维导图优势 思维导图融入大量java并发编程知识的同时,覆盖大量 ...

  10. 思维导图simplemind添加样式预设的方法

    在本篇文章中,我们介绍的是思维导图simplemind添加样式预设的方法.使用simplemind,可以隐藏或显示单个主题或分支 显示分支边框以突出显示分支. 使用复选框,交联和自动编号 搜索包含文本 ...

最新文章

  1. gradle新建工程,多项目依赖,聚合工程
  2. mysql memcached plugin插件
  3. 通俗介绍拉普拉斯变换,傅里叶变换和z变换
  4. Vista共享文件夹无法访问?
  5. 【算法】剑指 Offer 42. 连续子数组的最大和 【重刷】
  6. React Native 与 Flutter 的跨平台之战!
  7. 【贪玩巴斯】无线传感器网络(二)「无线传感器网络中物理层的五点详解」 2021-09-24
  8. Python压缩解压zip文件
  9. 第三章 群体用户画像分析
  10. OSChina 周三乱弹 ——carlos 你和你的电脑怎么过的幸福?
  11. element-ui MessageBox弹框确定和取消位置(this.$confirm)
  12. 在线TSV转SQL工具
  13. 创业之前你需要了解的九个真相
  14. 名字大作战V2.0!
  15. Zcash的“十月围城”
  16. HDU多校6821A Very Easy Graph Proble
  17. Matlab概率论与数理统计实践-事件发生的概率的三种计算方法:二项分布公式,正态分布,切比雪夫不等式
  18. 表格内容拆分怎么做?
  19. 通达信网上交易接口的运行结果
  20. 基于prometheus采集isilon(OneFS)存储指标

热门文章

  1. pycharm专业版账号登录问题
  2. python怎么读单词_利用Python制作查单词小程序(一):抓取来自百度翻译的单词释义和音标...
  3. 路由器和带宽猫、AP、AC、交换机
  4. 1.2、logistic回归之极大似然法
  5. 用PC机实现与ATV12变频器Modbus通信控制电机
  6. 8- 性能测试面试题(测试框架总结)史上最全面试题
  7. 编写USB鼠标驱动程序,并测试
  8. IDEA打包jar包的多种方式
  9. python实例008--摄氏温度和华氏温度转换
  10. centos进入管理员_centOS 如何让当前用户取得管理员权限