在GUI构建器中找到的对齐准则/捕捉的算法

时间:2017-01-25 10:59:37

标签: java android user-interface kotlin

我尝试实现与许多GUI构建器类似的行为:当前拖动的组件应该与另一个组件对齐,如果它们几乎在水平或垂直线上。 我目前的方法是迭代所有放置的组件,并检查四条边是否(几乎)与拖动组件的边缘对齐:

for (v in rootView.relativeLayout.children()) {
    // x
    val left = event.rawX - dXInit
    val right = event.rawX - dXInit + view.width
    val leftEdgeRange = (v.leftEdge() - 50 .. v.leftEdge() + 50)
    val rightEdgeRange = (v.rightEdge() - 50 .. v.rightEdge() + 50)

    when (left) {
        in leftEdgeRange -> x = v.leftEdge()
        in rightEdgeRange -> x = v.rightEdge()
    }
    when (right) {
        in leftEdgeRange -> x = v.leftEdge() - view.width
        in rightEdgeRange -> x = v.rightEdge() - view.width
    }
    // y
    val top = event.rawY - dYInit
    val bottom = event.rawY - dYInit + view.height
    val topEdgeRange = (v.topEdge() - 50 .. v.topEdge() + 50)
    val bottomEdgeRange = (v.bottomEdge() - 50 .. v.bottomEdge() + 50)

    when (top) {
        in topEdgeRange -> y = v.topEdge()
        in bottomEdgeRange -> y = v.bottomEdge()
    }
    when (bottom) {
        in topEdgeRange -> y = v.topEdge() - view.height
        in bottomEdgeRange -> y = v.bottomEdge() - view.height
    }
}

fun View.topEdge() = y
fun View.bottomEdge() = y + height
fun View.leftEdge() = x
fun View.rightEdge() = x + width

但这似乎效率低下,因为这是在 onTouch 中调用的,所以这个循环经常运行。有更好的方法吗?欢迎使用一般或Java答案。

1 个答案:

答案 0 :(得分:1)

更有效的方法是减少 O(n)循环,迭代所有视图到 O(log n)搜索最近的边缘一些排序的表示,如TreeSetTreeMapbalanced binary search trees。当然,这需要您为leftEdgerightEdgetopEdgebottomEdge中的每一个存储四个单独的已排序表示。

一个简单的例子是(仅显示leftEdge,其他类似):

val viewsByLeftEdge = TreeMap<Int, View>()

要在地图中添加视图,只需使用:

viewsByLeftEdge[view.leftEdge()] = view

(请注意,如果多个视图具有相同的左边缘值,则只有最后一个视图将存储在此地图中)

然后,您可以找到距离给定left坐标最近的左边缘,而不是遍历所有视图:

val floorL = viewsByLeftEdge.floorKey(left)
val ceilingL = viewsByLeftEdge.ceilingKey(left)
val nearestL = when {
    floorL == null -> ceilingL
    ceilingL == null -> floorL
    ceilingL - left < left - floorL -> ceilingL
    else -> floorL
}

if (nearestL in left - 50 .. left + 50)
    x = nearestL

此处,.floorKey(x)返回地图中小于或等于x的最高边坐标,如果没有此类坐标,则返回null。同样,.ceilingKey(x)会返回地图中的最低坐标,该坐标大于xnull

这需要 O(log n)时间,并且比任何大量视图的所有视图都要快。如果您不需要按其边缘获取视图,请将TreeMap<Int, View>替换为TreeSet<Int>来简化代码(函数则为floor(x)ceiling(x)

您可以将地图放在代码中,然后使用View填充它们,并为适合您代码设计的四条边组成函数。