Как центрировать текстовые представления в пользовательском индикаторе выполнения в Jetpack Compose

Я пытаюсь создать собственный индикатор выполнения в Compose, где каждый шаг представлен точкой, а под каждой точкой есть текстовая метка. Однако у меня возникли проблемы с выравниванием текстовых меток так, чтобы они были правильно центрированы под каждой точкой.

Это то, чего я пытаюсь достичь:

Вот моя текущая реализация и как она выглядит:

data class Step(val title: String = "") {}

StepsProgressView(
     steps = listOf(Step("text"),Step("something"),Step("some"),Step("DD"),Step("Ssss cccc")),
     completedIndex = 2
)

@Composable
fun StepsProgressView(
    steps: List<Step>,
    modifier: Modifier = Modifier,
    lineWidth: Dp = 1.dp,
    completedIndex: Int = 2
) {
    Column(modifier = modifier) {
        Box(contentAlignment = Alignment.Center) {
            Canvas(modifier = Modifier.fillMaxWidth()) {
                val width = drawContext.size.width
                val height = drawContext.size.height

                val yOffset = height / 2
                val itemWidth = width / steps.size

                var startOffset = itemWidth / 2
                var endOffset = startOffset

                val barWidth = lineWidth.toPx()
                repeat(steps.size - 1) { index ->
                    endOffset += itemWidth

                    drawLine(
                        brush = SolidColor(if (index < completedIndex) Color.Blue else Color.Gray),
                        start = Offset(startOffset, yOffset),
                        end = Offset(endOffset, yOffset),
                        strokeWidth = barWidth
                    )
                    startOffset = endOffset
                }
            }

            Row(
                Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceAround,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                repeat(steps.size) { index ->
                    Box(contentAlignment = Alignment.Center) {
                        // last index image
                        if (index == steps.lastIndex)
                            Image(
                                painter = painterResource(id = androidx.biometric.R.drawable.fingerprint_dialog_fp_icon),
                                contentDescription = null,
                                modifier = Modifier.size(24.dp),
                                contentScale = ContentScale.Crop
                            )
                        // circles box
                        else
                            Box(
                                modifier = Modifier
                                    .size(12.dp)
                                    .drawBehind {
                                        drawCircle(
                                            color =
                                              if (index <= completedIndex) Color.Blue
                                              else Color.Gray
                                        )
                                    },
                                contentAlignment = Alignment.Center,
                                content = {})
                    }
                }
            }
        }
        Spacer(Modifier.height(8.dp))

        Row(
            Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceAround
        ) {
            steps.forEachIndexed { index, step ->
                Text(
                    text = step.title,
                    style = TextStyle(
                          color = 
                             if (steps.indexOf(step) <= completedIndex) Color.Blue 
                             else Color.Gray,
                          fontSize = 13.sp,
                          textAlign = TextAlign.Center
                    ),
                    modifier = Modifier
                        .wrapContentWidth()
                        .weight(1f)
                )
            }
        }
    }
}

Проблема, с которой я столкнулся, заключается в том, что текстовые метки под точками не идеально выровнены по центру. Я хочу, чтобы каждая текстовая метка располагалась по центру под соответствующей точкой.

Любые предложения будут высоко оценены. Спасибо!

В настоящее время ваш код не совсем воспроизводим. Ваш HelpText Composable не включен, и мы не знаем, откуда взялся drawContext. Ваш подход с использованием модификатора weight выглядит нормально, тексты должны быть распределены равномерно. Я думаю, что реальный вопрос, с которым вы столкнулись, заключается в том, как сделать так, чтобы первая точка индикатора выполнения отображалась над центром первого текста. Или, наоборот, _как указать отступы слева от строки текста, чтобы первый текст располагался по центру под первой точкой индикатора выполнения. А не могли бы вы отредактировать свое сообщение и приложить скриншот?

BenjyTec 11.07.2024 16:41

«HelpText» — просто компонуемый пользовательский текст, исправлено. «drawContext» доступен из области Canvas. И вопрос, с которым я столкнулся, заключается в том, как сделать так, чтобы последние текстовые представления из индикатора выполнения отображались по центру под точками, потому что именно они смещены, отчасти потому, что последний индекс представляет собой изображение в два раза больше, чем другие точки. Утром отредактирую свой пост и выложу скриншот

Duddie 11.07.2024 22:01

Проблема вызвана тем, что последнее изображение в два раза больше остальных точек. Когда я выравниваю их до одинакового размера, тексты выравниваются идеально.

Duddie 12.07.2024 07:29
2
3
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что ваше последнее индексное изображение больше остальных кругов. Это нарушает расположение вашего ряда, которое зависит от weight(1f) равномерного распределения пространства.

Один из способов решения этой проблемы — сделать последнее индексное изображение того же размера, что и другие круги, а затем, тем не менее, нарисовать изображение желаемого размера:

// last index image
if (index == steps.lastIndex)
    Box(modifier = Modifier.size(12.dp)) {
        Image(
            painter = painterResource(id = R.drawable.barImage),
            contentDescription = null,
            modifier = Modifier.requiredSize(24.dp),
            contentScale = ContentScale.Crop
        )
    }
// circles box
else
    ...

Возможно, вам придется добавить дополнительное дополнение к строке, чтобы изображение, превышающее ожидаемое, поместилось.

Огромное спасибо, у меня появилась возможность попробовать!

Duddie 12.07.2024 08:04

Другие вопросы по теме