在画布上绘制线条

时间:2013-10-21 16:40:21

标签: android android-layout android-canvas

我正在为android开发思维导图工具。我正在为思维导图http://www.examtime.com/files/2013/08/How-to-create-an-online-mind-map.jpg创建这种类型的布局。有没有办法在运行时创建这些类型的行(在图像中创建)来连接对象。请帮忙

1 个答案:

答案 0 :(得分:1)

你应该研究三次贝塞尔曲线(样条曲线)。简单地使用Canvas就没有简单的方法。这是一个概述:

Bezier曲线使用2个点(P0 - 原点和P1 - 目的地)和2个矢量(V0 - 曲线离开P0的方向,V1 - 曲线进入P1的方向)。出于我们的目的,P0,P1,V0,V1都应该是PointF类型。

我们将使用t来表示路径上的位置。当t = 0时,位置为P0,当t = 1时,位置为P1。 0到1之间的任何t值都将沿着路径。 0 <= t <= 1。

现在,举个例子,让我们看一下将“Nobody完美”节点连接到“Tidy up later”节点的曲线。

在这种情况下,P0将是“Nobody's perfect”的右侧中间,而P1将是“Tidy up later”的底部中间侧。

我们将使两个向量垂直于它们要离开/进入的节点,因此V0的值将为{P1.x - P0.x,0}。该向量将指向右侧,并且其强度等于两个节点之间的距离。以类似的方式,我们将构造指向节点的V1向量:{0,P0.y - P1.y}

既然你有了矢量和点,你就会想要开始绘制曲线。要做到这一点,你将使用一些可分为1的小步进值迭代t,例如0.1,0.025,0.001等。让我们称这个值为“步”每次迭代都会在曲线上生成一个点,然后你我想在每个点之间连接一条线。

以下是此部分的代码示例:

PointF start, end;
for (float t = 0; t < 1; t += step)
{
    start = getBezierPosition(t);
    end = getBezierPosition(t + step)
    canvas.drawLine(start.x, start.y, end.x, end.y, paint);
}

现在,困难的部分 - 计算位置t的贝塞尔曲线的位置:

private PointF getBezierPosition(float t)
{
    PointF result = new PointF();

    float oneMinusT, x, y;  

    oneMinusT = 1 - t;
    x = oneMinusT * oneMinusT * oneMinusT * P0.x +
                3 * oneMinusT * oneMinusT * t * V0.x +
                3 * oneMinusT * t * t * V1.x +
                t * t * t * P1.x;
    y = oneMinusT * oneMinusT * oneMinusT * P0.y +
                3 * oneMinusT * oneMinusT * t * V0.y +
                3 * oneMinusT * t * t * V1.y +
                t * t * t * P1.y;

    result.set(x, y);

    return result;
}

这是三次贝塞尔曲线的公式。您可以详细了解here

我会让你实现确定P0&amp;位置的逻辑。 P1,和V0&amp;的方向V1(请记住,它们应始终垂直于它们要离开/进入的节点以获得最佳效果)。您需要使用逻辑来确定P所在节点的哪一侧,并且它很可能会链接到您应用于将节点定位在第一位的逻辑。

另外,为了获得最佳效果,请尝试使用您正在绘制的绘画的笔触宽度。例如,当t = 0时,使用大小为5的笔划宽度,当t = 1时,使用笔划大小3.确保在它们之间平滑迭代(因此,如果t = 0.5,笔划宽度将为4)

我会承认,这些东西在数学方面有点沉重,可能不会处于初学者的舒适区域,但是如果你想达到像你在那张照片中所展示的动态曲线,我恐怕你“会让你的手有点脏。”

祝你好运!让我知道结果如何:)