自定义布局之树形布局(一):效果展示及使用方法

先来展示一下效果吧。
在这里插入图片描述

基本使用

先导入此布局控件。

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一下。

下一篇将讲解树形布局的实现思路及自定义属性声明。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值