GUI 构建器中的对齐指南/捕捉算法

Ben*_*oyo 5 java user-interface android 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
    }
}
Run Code Online (Sandbox Code Playgroud)

fun View.topEdge() = y
fun View.bottomEdge() = y + height
fun View.leftEdge() = x
fun View.rightEdge() = x + width
Run Code Online (Sandbox Code Playgroud)

但这似乎效率低下,因为该代码是在 中调用的onTouch(),因此该循环运行得非常频繁。有更好的方法吗?欢迎一般或 Java 答案。

hot*_*key 2

更有效的方法是减少O(n)循环,该循环将所有视图迭代为O(log n)搜索某种排序表示中的最近边缘,例如 aTreeSet或 a TreeMap,它们是平衡二叉搜索树。当然,这要求您为leftEdgerightEdgetopEdge和中的每一个存储四个单独的排序表示bottomEdge

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

val viewsByLeftEdge = TreeMap<Int, View>()
Run Code Online (Sandbox Code Playgroud)

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

viewsByLeftEdge[view.leftEdge()] = view
Run Code Online (Sandbox Code Playgroud)

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

之后,您可以找到距离给定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
Run Code Online (Sandbox Code Playgroud)

此处,.floorKey(x)返回地图中小于或等于 的最高边缘坐标x,或者null如果没有这样的坐标。同样,.ceilingKey(x)返回地图中大于x、 或 的最低坐标null

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

您可以将地图放入代码中,用Views 填充它们,并为四个边编写更适合您的代码设计的函数。