先来展示一下效果吧。
基本使用
先导入此布局控件。
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
...
implementation 'com.github.PYJTLK:MindMapViewTest:1.3'
}
布局代码如下。
<com.pyjtlk.widget.TreeLayout
android:id="@+id/treeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#A00"
app:treeDirection="leftToRight"
app:levelInterval="50dp">
<!--树的根结点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A0A"/>
<!--子节点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A0A0"
android:layout_margin="10dp"/>
<!--子节点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A00A"
android:layout_margin="10dp"/>
<!--子树的布局-->
<com.pyjtlk.widget.TreeLayout
android:id="@+id/treeView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#A00"
app:treeDirection="leftToRight"
app:levelInterval="50dp">
<!--树的根结点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A0A"/>
<!--子节点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A0A0"
android:layout_margin="10dp"/>
<!--子节点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A00A"
android:layout_margin="10dp"/>
<!--子节点-->
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#A0A0"
android:layout_margin="10dp"/>
</com.pyjtlk.widget.TreeLayout>
</com.pyjtlk.widget.TreeLayout>
每个TreeLayout的第一个子控件为树的根结点。
布局摆放出来的效果如下,红色的为主树,橙色的为子树。
接着在java文件进行初始化。
private TreeLayout treeView;
private TreeLayout treeView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
treeView = findViewById(R.id.treeView);
//设置连接线绘制器
treeView.setLineDrawer(new DirectLineDrawer(6,Color.WHITE));
treeView1 = findViewById(R.id.treeView1);
//设置连接线绘制器
treeView1.setDecorDrawer(new DirectLineDrawer(6,Color.WHITE));
}
最后运行一下程序就可以了。
布局属性
属性 | 说明 |
---|---|
app:treeDirection | 树的绘制方向,有四种:leftToRight,rightToLeft,upToDown,downToUp |
app:levelInterval | 父结点与子结点的水平(竖直)间距 |
常用方法
方法名 | 说明 |
---|---|
setLevelInterval | 设置父结点与子结点的水平(竖直)间距 |
getLevelInterval | 获取父结点与子结点的水平(竖直)间距 |
setDecorDrawer | 设置装饰物绘制器,只会作用于本树的直接子结点,不会作用于子树的子结点 |
setUnionDecorDrawer | 设置装饰物绘制器,作用于本树及其子树 |
setTreeDirection | 设置树的方向,只会作用于本树,不会作用于子树 |
setUnionTreeDirection | 设置树的方向,作用于本树及其子树 |
lockTree | 锁定此布局,如果解锁则可以拖拽此布局 |
isLocked | 获取锁定状态 |
dfs | 深度遍历子控件 |
bfs | 广度遍历子控件 |
loadViewsFormData | 通过数据加载子控件 |
进阶用法
如果对结点的连接线不满意,我们也可以自己来绘制连接线,就像下面这样。
需要做的就是写一个连接线绘制器的子类,代码如下。
DirectLineDrawer是TreeLayout的一个静态抽象内部类,重写它就可以绘制自己想要的连接线了。
public class DocumentLineDrawer extends DirectLineDrawer{
private int mLevelInterval;
public DocumentLineDrawer(int lineWidth,int levelInterval,int color) {
super(lineWidth, color);
mLevelInterval = levelInterval;
}
@Override
protected void onDrawDecorator(Canvas canvas, Paint paint, Rect start, Rect end, int direction) {
paint.setColor(getColor());
paint.setStyle(Paint.Style.FILL);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(getLineWidth());
switch(direction){
case Direction.DIRECTION_LEFT_TO_RIGHT:
mStartX = start.right;
mEndX = end.left;
mStartY = (start.top + start.bottom) / 2;
mEndY = (end.top + end.bottom) /2;
onDrawLineLeftToRight(canvas,paint,mStartX,mStartY,mEndX,mEndY);
break;
case Direction.DIRECTION_RIGHT_TO_LEFT:
mStartX = start.left;
mEndX = end.right;
mStartY = (start.top + start.bottom) / 2;
mEndY = (end.top + end.bottom) /2;
onDrawLineRightToLeft(canvas,paint,mStartX,mStartY,mEndX,mEndY);
break;
case Direction.DIRECTION_UP_TO_DOWN:
mStartX = (start.left + start.right) / 2;
mEndX = (end.left + end.right) / 2;
mStartY = start.bottom;
mEndY = end.top;
onDrawLineUpToDown(canvas,paint,mStartX,mStartY,mEndX,mEndY);
break;
case Direction.DIRECTION_DOWN_TO_UP:
mStartX = (start.left + start.right) / 2;
mEndX = (end.left + end.right) / 2;
mStartY = start.top;
mEndY = end.bottom;
onDrawLineDownToUp(canvas,paint,mStartX,mStartY,mEndX,mEndY);
break;
}
}
protected void onDrawLineUpToDown(Canvas canvas, Paint paint,int startX,int startY,int endX,int endY) {
canvas.drawLine(startX,startY,startX,startY + mLevelInterval / 2,paint);
canvas.drawLine(startX,startY + mLevelInterval / 2,endX,
startY + mLevelInterval / 2,paint);
canvas.drawLine(endX,startY + mLevelInterval / 2,endX,
endY,paint);
}
protected void onDrawLineDownToUp(Canvas canvas, Paint paint,int startX,int startY,int endX,int endY) {
canvas.drawLine(startX,startY,startX,startY - mLevelInterval / 2,paint);
canvas.drawLine(startX,startY - mLevelInterval / 2,endX,
startY - mLevelInterval / 2,paint);
canvas.drawLine(endX,startY - mLevelInterval / 2,endX,
endY,paint);
}
protected void onDrawLineLeftToRight(Canvas canvas, Paint paint,int startX,int startY,int endX,int endY){
canvas.drawLine(startX,startY,startX + mLevelInterval / 2,startY,paint);
canvas.drawLine(startX + mLevelInterval / 2,startY,startX + mLevelInterval / 2,
endY,paint);
canvas.drawLine(startX + mLevelInterval / 2,endY,endX,
endY,paint);
}
protected void onDrawLineRightToLeft(Canvas canvas, Paint paint,int startX,int startY,int endX,int endY){
canvas.drawLine(startX,startY,startX - mLevelInterval / 2,startY,paint);
canvas.drawLine(startX - mLevelInterval / 2,startY,startX - mLevelInterval / 2,
endY,paint);
canvas.drawLine(startX - mLevelInterval / 2,endY,endX,
endY,paint);
}
public int getLevelInterval() {
return mLevelInterval;
}
public void setLevelInterval(int levelInterval) {
this.mLevelInterval = levelInterval;
}
}
接着设置一下就可以了。
treeView.setLineDrawer(new DocumentLineDrawer(6,treeView.getLevelInterval(),Color.WHITE));
如果想拖拽此布局,解锁就行了,效果如下。
treeView.lockTree(false);
当然,树形布局还可以动态加载子控件,不过首先需要对Tree的基本使用有所了解。
把设置好的Tree数据和子控件加载器(继承自 TreeContentLoader 类)传入 loadViewsFormData 即可。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ClassicDecoratorFactory factory = new ClassicDecoratorFactory(6,12,150, Color.WHITE);
treeLayout = findViewById(R.id.treeLayout);
treeLayout.setDecorDrawer(factory.createDecorator());
Tree<String> tree = Tree.parseFromXml(getResources().openRawResource(R.raw.tree_string),new StringTreeXmlHandler());
treeLayout.lockTree(true);
treeLayout.loadViewsFormData(tree,new MyTreeContentLoader());
}
这样树形布局就可以动态地载入子控件了。
最后
感兴趣的朋友可以到Github项目里看看,喜欢的话记得star一下。
下一篇将讲解树形布局的实现思路及自定义属性声明。