Android自定义脑图布局

本文介绍了一个名为TreeLayout的自定义ViewGroup,它允许用户通过ScaleGestureDetector实现对脑图布局的缩放操作。通过监听scale因子的变化,视图可以平滑调整其缩放比例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class TreeLayout extends ViewGroup {
Tree tree;
int mDy = 60;
int mDx = 100;
private ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
double scaleFactor = detector.getScaleFactor();
float old = TreeLayout.this.getScaleX();
if (Math.abs(scaleFactor - old) > 0.6 || Math.abs(scaleFactor - old) < 0.02) {
//忽略
} else {
TreeLayout.this.setScaleX(detector.getScaleFactor());
TreeLayout.this.setScaleY(detector.getScaleFactor());
}
return false;
}

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }
});
private int lastX = 0;
private int lastY = 0;

private int mode = 0;

@Override
public boolean onTouchEvent(MotionEvent event) {
    int currentX = (int) event.getRawX();//获得手指当前的坐标,相对于屏幕
    int currentY = (int) event.getRawY();

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mode = 1;
            break;
        case MotionEvent.ACTION_UP:
            mode = 0;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            //将模式进行为负数这样,多指下,抬起不会触发移动
            mode = -2;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            mode += 1;
            break;

        case MotionEvent.ACTION_MOVE:
            if (mode >= 2) {
                //缩放
                //mScaleGestureDetector.onTouchEvent(event);

            } else if (mode == 1) {
                int deltaX = currentX - lastX;
                int deltaY = currentY - lastY;

                int translationX = (int) getTranslationX() + deltaX;
                int translationY = (int) getTranslationY() + deltaY;

               setTranslationX(translationX);
               setTranslationY(translationY);
            }
            break;
    }

    lastX = currentX;
    lastY = currentY;

    return  mScaleGestureDetector.onTouchEvent(event);
}

public TreeLayout(Context context) {
    super(context);
}

public TreeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public TreeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

public TreeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);

}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = getChildCount();
    for (int i = 0; i < size; i++) {
        measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
    }
    mCurLine = 0;
    minTop = Integer.MAX_VALUE;
    maxBottom = 0;
    // 深度搜索确定每一个节点的 top
    deep(this, tree.getTreeRoot(), mCurLine);

    minLeft = Integer.MAX_VALUE;
    maxRight = 0;
    List<TreeNode> childList = new ArrayList<>();
    childList.add(tree.getTreeRoot());
     // 深度搜索确定每一个节点的 start
    width(this, childList );
    setMeasuredDimension(maxRight - minLeft,maxBottom - minTop + 2 * mDy);
}

private int minLeft = Integer.MAX_VALUE;
private int maxRight = 0;
private void width(TreeLayout treeLayout, List<TreeNode> childList) {
    List<TreeNode> nextChildList = new ArrayList<>();
    for (TreeNode child : childList) {
        TreeNode parent = child.getParent();
        nextChildList.addAll(child.getChildNodes());
        if (parent == null) {
            View childView  = findNodeViewFromNodeModel(child);
            childView.layout(0, childView.getTop(), 0 + childView.getMeasuredWidth(), childView.getBottom());
            minLeft = 0;
            continue;
        }
        View parentView = findNodeViewFromNodeModel(parent);
        View childView  = findNodeViewFromNodeModel(child);

        childView.layout(parentView.getRight() + mDx, childView.getTop(),parentView.getRight() + mDx + childView.getMeasuredWidth(), childView.getBottom());

        if (minLeft > parentView.getRight() + mDx) {
            minLeft = parentView.getRight() + mDx;
        }

        if (maxRight < parentView.getRight() + mDx + childView.getMeasuredWidth()) {
            maxRight = parentView.getRight() + mDx + childView.getMeasuredWidth();
        }

    }
    if (nextChildList.size() > 0) {
        width(treeLayout, nextChildList);
    }
}

private int mCurLine = 0;
private int minTop = Integer.MAX_VALUE;
private int maxBottom = 0;
private void deep(TreeLayout treeView, TreeNode<String> rootNode, int curLine) {
    if (rootNode.getChildNodes().size() == 0) {
        View leafView = treeView.findNodeViewFromNodeModel(rootNode);
        leafView.layout(leafView.getLeft(), curLine + mDy,leafView.getRight(), curLine + mDy + leafView.getMeasuredHeight());
        mCurLine = curLine + mDy + leafView.getMeasuredHeight();
        if (minTop > curLine + mDy) {
            minTop = curLine + mDy;
        }
        if (maxBottom < curLine + mDy + leafView.getMeasuredHeight()) {
            maxBottom = curLine + mDy + leafView.getMeasuredHeight();
        }
        return;
    }
    for ( TreeNode<String> curNode : rootNode.getChildNodes()) {
        deep(treeView, curNode, mCurLine);
    }


    View rootView = treeView.findNodeViewFromNodeModel(rootNode);
    View firstView = treeView.findNodeViewFromNodeModel(rootNode.getChildNodes().get(0));
    View lastView = treeView.findNodeViewFromNodeModel(rootNode.getChildNodes().get(rootNode.getChildNodes().size() - 1));
    int mid = (lastView.getBottom() + firstView.getTop()) / 2;
    int top = mid - rootView.getMeasuredHeight() / 2;
    int bot = mid + rootView.getMeasuredHeight() / 2;
    rootView.layout(rootView.getLeft(), top, rootView.getRight(), bot);
    if (bot > mCurLine) {
        mCurLine = bot;
    }
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

}

@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (tree.getTreeRoot() != null) {
        drawTreeLine(canvas, tree.getTreeRoot());
    }
}

private void drawTreeLine(Canvas canvas, TreeNode<String> root) {
    TreeNoteView fatherView = (TreeNoteView) findNodeViewFromNodeModel(root);
    if (fatherView != null) {
        List<TreeNode> childNodes = root.getChildNodes();
        for (TreeNode<String> node : childNodes) {

            //连线
            drawLineToView(canvas, fatherView, findNodeViewFromNodeModel(node));

            //递归
            drawTreeLine(canvas, node);
        }
    }
}

private void drawLineToView(Canvas canvas, View from, View to) {
    if (to.getVisibility() == GONE) {
        return;
    }
    Paint mPaint = new Paint();
    mPaint.setStyle(Paint.Style.STROKE);

    float width = 2f;

    mPaint.setStrokeWidth(6);
    mPaint.setColor(Color.BLACK);

    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 mPath = new Path();
    mPath.reset();
    mPath.moveTo(formX, formY);
    mPath.quadTo(toX, toY, toX, toY);
    canvas.drawPath(mPath, mPaint);
}

public Tree getTree() {
    return tree;
}

public void setTree(Tree tree) {
    this.tree = tree;
    deepAddChildView(tree.getTreeRoot());
}

private void deepAddChildView(TreeNode treeRoot) {
    if (treeRoot == null) {
        return;
    }
    for (Object node : treeRoot.getChildNodes()) {
        TreeNode curNode = (TreeNode)node;
        deepAddChildView(curNode);
    }
    TreeNoteView treeNoteView = new TreeNoteView(getContext());
    treeNoteView.setTreeNode(treeRoot);
    addView(treeNoteView);
}

public View findNodeViewFromNodeModel(TreeNode<String> nodeModel) {
    View view = null;
    int size = getChildCount();
    for (int i = 0; i < size; i++) {
        View childView = getChildAt(i);
        if (childView instanceof TreeNoteView) {
            TreeNode<String> treeNode = ((TreeNoteView) childView).getTreeNode();
            if (treeNode == nodeModel) {
                view = childView;
                continue;
            }
        }
    }
    return view;
}

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值