kotlin ComposeUI:更方便的方式来创建这个布局,用线条连接几个可组合的对象

r1wp621o  于 2023-08-06  发布在  Kotlin
关注(0)|答案(2)|浏览(94)

我想要实现什么?

我想创建以下布局。重要的是,垂直的“连接”线要在编号的框内:
x1c 0d1x的数据

我已经尝试了什么(第一个,天真的方法)?

我使用一个可组合的和几行创建了布局。编号的框获取一个布尔参数,该参数指示框内的顶行还是底行被显示。也有垂直线之间的组合画垂直线。它会产生预期的结果并渲染此图像:



这是我使用的代码:

@Composable
fun ConnectedComposable(modifier: Modifier) {
    Column(modifier = modifier) {
        NumberedBox(
            number = 1,
            topLine = false,
            bottomLine = true
        )
        VerticalDivider(Modifier.height(64.dp))
        NumberedBox(
            number = 2,
            topLine = true,
            bottomLine = true
        )
        VerticalDivider(Modifier.height(64.dp))
        NumberedBox(
            number = 3,
            topLine = true,
            bottomLine = false
        )
    }
}

@Composable
fun ColumnScope.VerticalDivider(
    modifier: Modifier = Modifier
) {
    Divider(
        modifier = modifier
            .width(2.dp)
            .align(Alignment.CenterHorizontally),
        color = Color.Black
    )
}

@Composable
fun NumberedBox(
    number: Int,
    topLine: Boolean,
    bottomLine: Boolean,
    modifier: Modifier = Modifier
) {
    Surface(
        color = Color.LightGray,
        border = BorderStroke(1.dp, Color.Black),
        modifier = modifier
            .size(100.dp)
            .fillMaxSize()
    ) {
        Column(
            modifier = Modifier
                .padding(horizontal = 16.dp)
                .fillMaxSize()
        ) {
            if (topLine) {
                VerticalDivider(
                    Modifier.weight(1f)
                )
            }
            else {
                Spacer(modifier = Modifier.weight(1f))
            }

            Text(
                number.toString(),
                modifier = Modifier
                    .align(Alignment.CenterHorizontally)
                    .padding(vertical = 16.dp),
                textAlign = TextAlign.Center
            )

            if (bottomLine) {
                VerticalDivider(
                    Modifier.weight(1f)
                )
            }
            else {
                Spacer(modifier = Modifier.weight(1f))
            }
        }
    }
}

@Preview
@Composable
fun ConnectedComposable_Preview() {
    Scaffold {
        ConnectedComposable(
            Modifier
                .padding(it)
                .padding(16.dp)
        )
    }
}

字符串

我的问题

我不喜欢上面的代码,因为这几点:

  • 如果未来布局发生变化,就很难维持
  • 如果NumberedBox的大小不固定,则会变得更加复杂
  • 计算框内外行位置的代码必须保持同步
  • 如果行没有居中,则定位代码可能不是微不足道的。
    是否有其他/更好的方法来实现此布局?
ohfgkhjo

ohfgkhjo1#

第一个选项是将框和线作为可组合项并相应地对其进行布局,这对于开始布局和约束的人来说很容易。这将需要您添加NumberedBoxed和Dividers作为子可组合项。

@Composable
private fun DrawLineLayout(
    modifier: Modifier,
    content: @Composable () -> Unit
) {
    Layout(modifier = Modifier, 
           content = content) { measurables: List<Measurable>, constraints: Constraints ->

        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }

        // calculate max width and total height by offsetting 
        // VerticalDivider inside boxes
        // and x, y position of each Composable
        var totalHeight = 0
        val verticalPosition = mutableListOf<Int>()

        val maxWidth = placeables.maxOf { it.width }

        layout(maxWidth, totalHeight) {
            placeables.forEachIndexed() { index:Int, placeable: Placeable ->
                placeable.placeRelative(xPos, verticalPosition[index])
            }
        }
    }
}

字符串
第二个选项是使用SubcomposeLayout,我添加了样本here,这里是herehere,在使用SubcompoeLayout子合成并将这些位置传递给依赖内容后,将框绘制为mainConent并绘制覆盖。

@Composable
private fun LineDrawLayout(
    modifier: Modifier = Modifier,
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (List<Point>) -> Unit
) {

    SubcomposeLayout(modifier = modifier) { constraints ->
       // Implementation details
    }
}

enum class SlotsEnum { Main, Dependent }


并将框的中心返回到从属内容。然后,您可以在这些位置之间绘制线条,同时考虑文本的宽度。如果你愿意的话,你可以用文本(onTextLayout)和行高来做。

LineDrawLayout(
    mainContent = {
      // Your content is here
      NumberedBox(...)
      NumberedBox(...)
      ....
    },
    dependentContent = { points: List<Point> ->
      // This is overlay for drawing lines
      // Can be Canvas or Box with `Modifier.drawWithContent{}`
     
    }
)

qv7cva1a

qv7cva1a2#

我希望这对你有帮助。你需要根据自己的情况进行修改。[未测试]

@Composable
    fun ConnectedComposable(modifier: Modifier) {
        val data = listOf(
            NumberedBoxData(1, false, true),
            NumberedBoxData(2, true, true),
            NumberedBoxData(3, true, false)
        )
    
        LazyColumn(modifier = modifier) {
            items(data.size) { index ->
                NumberedBox(data[index])
                if (index < data.size - 1) {
                    VerticalDivider(Modifier.height(64.dp))
                }
            }
        }
    }
    
    data class NumberedBoxData(
        val number: Int,
        val topLine: Boolean,
        val bottomLine: Boolean
    )
    
    @Composable
    fun VerticalDivider(
        modifier: Modifier = Modifier
    ) {
        Divider(
            modifier = modifier
                .width(2.dp)
                .align(Alignment.CenterHorizontally),
            color = Color.Black
        )
    }
    
    @Composable
    fun NumberedBox(data: NumberedBoxData) {
        Surface(
            color = Color.LightGray,
            border = BorderStroke(1.dp, Color.Black),
            modifier = Modifier
                .fillMaxWidth()
        ) {
            Column(
                modifier = Modifier
                    .padding(horizontal = 16.dp)
                    .fillMaxSize()
            ) {
                if (data.topLine) {
                    VerticalDivider(Modifier.fillMaxWidth())
                }
    
                Text(
                    data.number.toString(),
                    modifier = Modifier
                        .align(Alignment.CenterHorizontally)
                        .padding(vertical = 16.dp),
                    textAlign = TextAlign.Center
                )
    
                if (data.bottomLine) {
                    VerticalDivider(Modifier.fillMaxWidth())
                }
            }
        }
    }
    
    @Preview
    @Composable
    fun ConnectedComposable_Preview() {
        Scaffold {
            ConnectedComposable(
                Modifier
                    .padding(it)
                    .padding(16.dp)
            )
        }
    }

字符串

相关问题