Forráskód Böngészése

修改连接线的颜色

bjb 5 napja
szülő
commit
ff4300f573

+ 8 - 8
app/src/main/java/com/iscs/bozzys/ui/pages/edit/step/PageEditStep.kt

@@ -8,8 +8,6 @@ import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateMapOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -19,7 +17,8 @@ import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import com.iscs.bozzys.ui.base.PageBase
-import com.iscs.bozzys.ui.pages.edit.step.compose.ConnectionLayer
+import com.iscs.bozzys.ui.pages.edit.step.compose.Anchor
+import com.iscs.bozzys.ui.pages.edit.step.compose.Connection
 import com.iscs.bozzys.ui.pages.edit.step.compose.Node
 import com.iscs.bozzys.ui.pages.edit.step.compose.NodeItem
 import com.iscs.bozzys.ui.pages.edit.step.compose.ZoomPanContainer
@@ -35,17 +34,18 @@ class PageEditStep : PageBase() {
     @Composable
     override fun GetViews(pv: PaddingValues) {
         val nodes = remember { mutableStateMapOf<String, Node>() }
-        val lines = listOf("A" to "B", "A" to "D", "B" to "C")
-        val ready by remember { derivedStateOf { nodes.isNotEmpty() } }
+        val lines = listOf(
+            Connection("A", "B"),
+            Connection("A", "D", fromAnchor = Anchor.RIGHT, toAnchor = Anchor.LEFT),
+            Connection("B", "C")
+        )
         LaunchedEffect(Unit) {
             nodes["A"] = Node("A", Offset(50f, 0f), Size(80))
             nodes["B"] = Node("B", Offset(250f, 0f), Size(80))
             nodes["B"] = Node("D", Offset(250f, 0f), Size(80))
             nodes["C"] = Node("C", Offset(450f, 0f), Size(80))
         }
-        ZoomPanContainer(modifier = Modifier.fillMaxSize()) {
-            // 连线操作
-            if (ready) ConnectionLayer(nodes, lines)
+        ZoomPanContainer(nodes, lines, modifier = Modifier.fillMaxSize()) {
             // 控件显示
             Box(
                 modifier = Modifier

+ 113 - 34
app/src/main/java/com/iscs/bozzys/ui/pages/edit/step/compose/Node.kt

@@ -16,56 +16,48 @@ import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInParent
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.toSize
 import androidx.compose.ui.zIndex
+import com.iscs.bozzys.ui.theme.Main
 
 /**
  * 布局之间的连线操作(贝塞尔曲线)
  */
 @Composable
-fun ConnectionLayer(
-    nodes: Map<String, Node>,
-    connections: List<Pair<String, String>>
-) {
+fun ConnectionLayer(nodes: Map<String, Node>, connections: List<Connection>, scale: Float) {
     Canvas(
         modifier = Modifier
             .fillMaxSize()
             .zIndex(0f)
     ) {
-        connections.forEach { (fromId, toId) ->
-            val from = nodes[fromId]
-            val to = nodes[toId]
-            if (from != null && to != null) {
+        connections.forEach { conn ->
+            val from = nodes[conn.fromId]
+            val to = nodes[conn.toId]
 
-                val start = from.rightCenter()
-                val end = to.leftCenter()
+            if (from != null && to != null) {
 
-                val control1 = Offset(
-                    start.x + 100f,
-                    start.y
-                )
-                val control2 = Offset(
-                    end.x - 100f,
-                    end.y
+                val pathPoints = orthogonalPath(
+                    fromNode = from,
+                    fromAnchor = conn.fromAnchor,
+                    toNode = to,
+                    toAnchor = conn.toAnchor
                 )
 
                 val path = Path().apply {
-                    moveTo(start.x, start.y)
-                    cubicTo(
-                        control1.x, control1.y,
-                        control2.x, control2.y,
-                        end.x, end.y
-                    )
+                    moveTo(pathPoints.first().x, pathPoints.first().y)
+                    for (i in 1 until pathPoints.size) {
+                        lineTo(pathPoints[i].x, pathPoints[i].y)
+                    }
                 }
 
                 drawPath(
-                    path = path,
-                    color = Color.Gray,
-                    style = Stroke(width = 3f)
+                    path = path, color = Main, style = Stroke(width = 6f / scale, cap = StrokeCap.Round, join = StrokeJoin.Round)
                 )
             }
         }
@@ -95,17 +87,87 @@ fun NodeItem(
     }
 }
 
-/**
- * 右边连线
- */
-fun Node.rightCenter(): Offset =
-    Offset(offset.x + size.width, offset.y + size.height / 2)
+fun Node.anchor(anchor: Anchor): Offset = when (anchor) {
+    Anchor.LEFT -> Offset(offset.x, offset.y + size.height / 2)
+    Anchor.RIGHT -> Offset(offset.x + size.width, offset.y + size.height / 2)
+    Anchor.TOP -> Offset(offset.x + size.width / 2, offset.y)
+    Anchor.BOTTOM -> Offset(offset.x + size.width / 2, offset.y + size.height)
+}
 
 /**
- * 左边连线
+ * 绘制正交连接线
  */
-fun Node.leftCenter(): Offset =
-    Offset(offset.x, offset.y + size.height / 2)
+private fun orthogonalPath(fromNode: Node, fromAnchor: Anchor, toNode: Node, toAnchor: Anchor, padding: Float = 40f): List<Offset> {
+    val from = fromNode.anchor(fromAnchor)
+    val to = toNode.anchor(toAnchor)
+    // 处理连接的点位
+    val points = mutableListOf<Offset>()
+    // 起点出线
+    var fromOut = when (fromAnchor) {
+        Anchor.LEFT -> Offset(from.x - padding, from.y)
+        Anchor.RIGHT -> Offset(from.x + padding, from.y)
+        Anchor.TOP -> Offset(from.x, from.y - padding)
+        Anchor.BOTTOM -> Offset(from.x, from.y + padding)
+    }
+    // 终点入线
+    var toIn = when (toAnchor) {
+        Anchor.LEFT -> Offset(to.x - padding, to.y)
+        Anchor.RIGHT -> Offset(to.x + padding, to.y)
+        Anchor.TOP -> Offset(to.x, to.y - padding)
+        Anchor.BOTTOM -> Offset(to.x, to.y + padding)
+    }
+
+
+    val midPoints = mutableListOf<Offset>()
+    // 绘制中间连线
+    if (fromAnchor == Anchor.BOTTOM) {
+        when (toAnchor) {
+            // 只需要绘制一个点
+            Anchor.LEFT -> midPoints += Offset(fromOut.x, toIn.y)
+            // 顶部需要绘制两个点才能变成直线
+            Anchor.TOP -> {
+                val y = if (toIn.y - fromOut.y < 0) {
+                    fromOut = fromOut.copy(y = from.y)
+                    toIn = toIn.copy(y = to.y)
+                    from.y + (to.y - from.y) / 2
+                } else {
+                    fromOut.y + (toIn.y - fromOut.y) / 2
+                }
+                midPoints += Offset(fromOut.x, y)
+                midPoints += Offset(toIn.x, y)
+            }
+
+            else -> {}
+        }
+    } else if (fromAnchor == Anchor.RIGHT) {
+        when (toAnchor) {
+            Anchor.LEFT -> {
+                val x = if (toIn.x - fromOut.x < 0) {
+                    fromOut = fromOut.copy(x = from.x)
+                    toIn = toIn.copy(x = to.x)
+                    from.x + (to.x - from.x) / 2
+                } else {
+                    fromOut.x + (toIn.x - fromOut.x) / 2
+                }
+                midPoints += Offset(x, fromOut.y)
+                midPoints += Offset(x, toIn.y)
+            }
+
+            else -> {}
+        }
+    }
+
+    // 将起点和起点出线绘制好
+    points += from
+    points += fromOut
+    // 绘制中间点
+    points += midPoints
+    // 绘制结束连线
+    points += toIn
+    points += to
+
+    return points
+}
 
 /**
  * 节点定义
@@ -115,3 +177,20 @@ data class Node(
     val offset: Offset,
     val size: Size
 )
+
+/**
+ * 定义连线位置
+ */
+enum class Anchor {
+    LEFT, RIGHT, TOP, BOTTOM
+}
+
+/**
+ * 定义连线信息
+ */
+data class Connection(
+    val fromId: String,
+    val toId: String,
+    val fromAnchor: Anchor = Anchor.RIGHT,
+    val toAnchor: Anchor = Anchor.LEFT
+)

+ 4 - 0
app/src/main/java/com/iscs/bozzys/ui/pages/edit/step/compose/ZoomContainer.kt

@@ -27,6 +27,8 @@ import kotlin.math.floor
  */
 @Composable
 fun ZoomPanContainer(
+    nodes: Map<String, Node>,
+    connections: List<Connection>,
     modifier: Modifier = Modifier,
     content: @Composable BoxScope.() -> Unit
 ) {
@@ -56,6 +58,8 @@ fun ZoomPanContainer(
         ) {
             // 点阵背景
             InfiniteDotBackground(scale = scale, offset = offset)
+            // 连接线操作
+            if (nodes.isNotEmpty()) ConnectionLayer(nodes, connections, scale = scale)
             // 外部自定义子控件
             content()
         }