Compose Shape Slider

本文详细介绍了如何在Compose中实现自定义Material3风格的Slider,包括Slider的属性、默认实现与难点,以及如何通过ShapeDrawable和Modifier实现圆角效果,涉及Track、Thumb的绘制和颜色定制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Compose 提供了 Slider 替代 View中的ProgressBar. ProgressBar 可以通过自定义 drawable 实现丰富的外观效果, 特别是是通过ShapeDrawable 实现圆角效果。

Compose 中默认提供的Slider 是Materail3 风格的,要想实现 Android View中的自定义风格 需要实现自己的Track 和Thumb Compose 函数。

一、Compose Materail3 Slider

Slider的属性

Slider是Android Jetpack Compose中的一个控件,用于实现滑动条的功能。

它具有以下常用的属性:

  1. value:滑动条的当前值。可以使用value参数来设置初始值,并通过onValueChange参数监听值的变化。

  2. onValueChange:滑动条值变化时的回调函数。可以在这个回调函数中处理滑动条值的更新逻辑。

  3. valueRange:滑动条的值范围。通过valueRange参数可以设置滑动条的最小值和最大值,如valueRange = 0f…100f。

  4. steps:滑动条的步长。可以使用steps参数设置滑动条每次滑动时的增量。

  5. modifier:用于修改滑动条的外观和行为的修饰符。例如,可以使用Modifier.height(48.dp)来设置滑动条的高度。

  6. colors:用于自定义Slider的颜色。可以设置滑块、激活轨道和非激活轨道的颜色。

Slider 的实现 SliderImpl

通过试用Compose Slider 以后发现默认的Slider 无法实现圆角效果。看了下Google的默认实现,

  1. draggableState gestureEndAction press drag 这几个Modifier 修饰符 完成触摸操作,进度条计算,刻度计算等。
  2. Layout 函数完成 Track和Thumb 的宽高和位置的计算。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SliderImpl(
    modifier: Modifier,
    enabled: Boolean,
    interactionSource: MutableInteractionSource,
    onValueChange: (Float) -> Unit,
    onValueChangeFinished: (() -> Unit)?,
    steps: Int,
    value: Float,
    valueRange: ClosedFloatingPointRange<Float>,
    thumb: @Composable (SliderPositions) -> Unit,
    track: @Composable (SliderPositions) -> Unit
) {
   
    val onValueChangeState = rememberUpdatedState<(Float) -> Unit> {
   
        if (it != value) {
   
            onValueChange(it)
        }
    }

    val tickFractions = remember(steps) {
   
        stepsToTickFractions(steps)
    }

    val thumbWidth = remember {
    mutableStateOf(ThumbWidth.value) }
    val totalWidth = remember {
    mutableStateOf(0) }

    fun scaleToUserValue(minPx: Float, maxPx: Float, offset: Float) =
        scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)

    fun scaleToOffset(minPx: Float, maxPx: Float, userValue: Float) =
        scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)

    val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
    val rawOffset = remember {
    mutableStateOf(scaleToOffset(0f, 0f, value)) }
    val pressOffset = remember {
    mutableStateOf(0f) }
    val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)

    val positionFraction = calcFraction(valueRange.start, valueRange.endInclusive, coerced)
    val sliderPositions = remember {
    SliderPositions(positionFraction, tickFractions) }
    sliderPositions.positionFraction = positionFraction
    sliderPositions.tickFractions = tickFractions

    val draggableState = remember(valueRange) {
   
        SliderDraggableState {
   
            val maxPx = max(totalWidth.value - thumbWidth.value / 2, 0f)
            val minPx = min(thumbWidth.value / 2, maxPx)
            rawOffset.value = (rawOffset.value + it + pressOffset.value)
            pressOffset.value = 0f
            val offsetInTrack = snapValueToTick(rawOffset.value, tickFractions, minPx, maxPx)
            onValueChangeState.value.invoke(scaleToUserValue(minPx, maxPx, offsetInTrack))
        }
    }

    val gestureEndAction = rememberUpdatedState {
   
        if (!draggableState.isDragging) {
   
            // check isDragging in case the change is still in progress (touch -> drag case)
            onValueChangeFinished?.invoke()
        }
    }

    val press = Modifier.sliderTapModifier(
        draggableState,
        interactionSource,
        totalWidth.value,
        isRtl,
        rawOffset,
        gestureEndAction,
        pressOffset,
        enabled
    )

    val drag = Modifier.draggable(
        orientation = Orientation.Horizontal,
        reverseDirection = isRtl,
        enabled = enabled,
        interactionSource = interactionSource,
        onDragStopped = {
    _ -> gestureEndAction.value.invoke() },
        startDragImmediately = draggableState.isDragging,
        state = draggableState
    )

    Layout(
        {
   
            Box(modifier = Modifier.layoutId(SliderComponents.THUMB)) {
    thumb(sliderPositions) }
            Box(modifier = Modifier.layoutId(SliderComponents
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值