You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中如何限制两个重量选择器的数值范围?

实现目标体重不大于当前体重的逻辑

核心思路

通过将体重转换为总克数统一比较,避免分别处理千克和克的复杂逻辑,然后根据当前选择的是「当前体重」还是「目标体重」,动态限制选择器的范围:

  • 选择目标体重时,最大值为「当前体重 - 1克」
  • 选择当前体重时,最小值为「目标体重 + 1克」

具体实现步骤

1. 添加辅助转换函数

先写两个工具函数,用于在「千克+克」和「总克数」之间转换,简化比较逻辑:

// 将kg和gr转换为总克数,处理null参数的默认值
private fun toTotalGrams(kg: Int?, gr: Int?): Int {
    val safeKg = kg ?: 35
    val safeGr = gr ?: 0
    return safeKg * 1000 + safeGr
}

// 将总克数拆分为kg和gr的Pair
private fun fromTotalGrams(totalGrams: Int): Pair<Int, Int> {
    val kg = totalGrams / 1000
    val gr = totalGrams % 1000
    return kg to gr
}

2. 修改组件状态初始化

不再使用固定的35kg初始值,而是根据选择类型加载对应已保存的体重,同时确保初始值在允许范围内:

@Composable
fun DailyCalorieWeightPickerComponent(
    onConfirmClick: (Int, Int) -> Unit,
    onCancelClick: () -> Unit,
    currentKg: Int?,
    currentGr: Int?,
    goalKg: Int?,
    goalGr: Int?,
    isClickGoalWeight: Boolean
) {
    // 基础重量范围:35kg(35000g) ~ 200kg(200000g)
    const val MIN_TOTAL_GRAMS = 35 * 1000
    const val MAX_TOTAL_GRAMS = 200 * 1000

    // 计算允许的总重量范围
    val (minAllowedTotal, maxAllowedTotal) = if (isClickGoalWeight) {
        // 目标体重:最大为当前体重减1克,最小不低于基础最小值
        val currentTotal = toTotalGrams(currentKg, currentGr)
        MIN_TOTAL_GRAMS to (currentTotal - 1).coerceAtLeast(MIN_TOTAL_GRAMS)
    } else {
        // 当前体重:最小为目标体重加1克,最大不超过基础最大值
        val goalTotal = toTotalGrams(goalKg, goalGr)
        (goalTotal + 1).coerceAtMost(MAX_TOTAL_GRAMS) to MAX_TOTAL_GRAMS
    }

    // 初始化并校验初始值在允许范围内
    val initialTotal = if (isClickGoalWeight) {
        toTotalGrams(goalKg, goalGr)
    } else {
        toTotalGrams(currentKg, currentGr)
    }
    val validInitialTotal = initialTotal.coerceIn(minAllowedTotal, maxAllowedTotal)
    val (validInitialKg, validInitialGr) = fromTotalGrams(validInitialTotal)

    var kg by remember { mutableStateOf(validInitialKg) }
    var gr by remember { mutableStateOf(validInitialGr) }

3. 动态计算选择器范围

根据当前选中的千克数,动态计算克数的可选范围,确保总重量不超出限制:

// 计算kg的可选范围
    val kgRange = run {
        val (minKg, _) = fromTotalGrams(minAllowedTotal)
        val (maxKg, _) = fromTotalGrams(maxAllowedTotal)
        minKg..maxKg
    }

    // 计算当前kg对应的gr可选范围
    val currentGrRange = run {
        val currentKgTotal = kg * 1000
        val minGr = if (kg == kgRange.first) {
            minAllowedTotal - currentKgTotal
        } else {
            0
        }
        val maxGr = if (kg == kgRange.last) {
            maxAllowedTotal - currentKgTotal
        } else {
            999 // 修正:gr范围应为0~999,避免出现1000g等于1kg的重复计算
        }
        minGr..maxGr
    }

4. 更新选择器的交互逻辑

修改NumberPicker的范围和值变更回调,确保选择的重量始终符合规则:

Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Bottom
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.Gray)
                .padding(top = 30.dp),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            NumberPicker(
                value = kg,
                onValueChange = { newKg ->
                    kg = newKg
                    // kg变化时,自动将gr调整到当前kg允许的范围内
                    gr = gr.coerceIn(currentGrRange)
                },
                range = kgRange
            )
            Text(text = "kg")
            NumberPicker(
                value = gr,
                onValueChange = { newGr ->
                    gr = newGr.coerceIn(currentGrRange)
                },
                range = currentGrRange
            )
            Text(text = "gr")
        }

        // 底部确认/取消按钮部分保持不变
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .height(IntrinsicSize.Min)
                .background(Color.Gray)
                .padding(vertical = 15.dp),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            TextButton(onClick = onCancelClick) {
                Text(
                    text = "cancel",
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Bold,
                    color = DefaultDYTColor,
                    modifier = Modifier.padding(8.dp)
                )
            }
            Divider(
                color = Color.White,
                modifier = Modifier
                    .padding(8.dp)
                    .fillMaxHeight()
                    .width(1.dp)
            )
            TextButton(onClick = { onConfirmClick(kg, gr) }) {
                Text(
                    text = "okey",
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Bold,
                    color = DefaultDYTColor,
                    modifier = Modifier.padding(8.dp)
                )
            }
        }
    }
}

关键细节说明

  • 修正了gr的范围为0~999,避免出现1000g等于1kg的逻辑错误
  • 使用coerceIn确保所有选择值都在允许范围内,包括初始值和用户操作后的数值
  • 通过总克数统一比较,避免了分别判断kg和gr的复杂逻辑,代码更简洁可靠

内容的提问来源于stack exchange,提问作者NewPartizal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:18:12