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

Android BottomSheet随滑动联动的抓取手柄SVG动画实现

BottomSheet抓取手柄滑动联动形态实现方案

核心实现思路

无需引入第三方库,也不使用固定时长的AnimatedVectorDrawable,直接复用BottomSheetBehavior.BottomSheetCallback中已经可以正常获取的slideOffset参数(取值范围0~1,0代表完全折叠、1代表完全展开),通过路径实时形变计算,让手柄的形态过渡进度和用户滑动操作100%同步,完全匹配交互预期。

落地步骤

1. 前置资源对齐

先调整你已有的两个Vector资源,满足平滑形变的基础要求:

  • 两个Vector的视口尺寸(viewportWidth/viewportHeight)、描边宽度、描边颜色、线帽/线接样式完全一致
  • 两个路径的节点数、绘制指令顺序完全一致,仅点位坐标存在差异。例:

    折叠态扁平横条路径不要用两点结构,改成和展开态V形一致的三点结构:M6 12 L12 12 L18 12,展开态V形路径为M6 10 L12 14 L18 10,二者都是3个绘制节点,形变时不会出现跳变。

2. 代码实现

直接在Fragment中编写逻辑,核心代码如下(Kotlin实现,Java写法逻辑完全一致):

class YourBottomSheetFragment : Fragment() {
    // 路径计算相关成员变量
    private val collapsedHandlePath = Path()
    private val expandedHandlePath = Path()
    private val currentHandlePath = Path()
    private val pathEvaluator = PathEvaluatorCompat() // AndroidX兼容类,支持API14+

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 初始化两个状态的路径,坐标和你的Vector资源视口对齐
        val viewportSize = 24f
        // 折叠态:扁平横条
        collapsedHandlePath.apply {
            reset()
            moveTo(6f, 12f)
            lineTo(12f, 12f)
            lineTo(18f, 12f)
        }
        // 展开态:向下V形箭头
        expandedHandlePath.apply {
            reset()
            moveTo(6f, 10f)
            lineTo(12f, 14f)
            lineTo(18f, 10f)
        }

        // 初始化BottomSheet和手柄控件
        val bottomSheet = view.findViewById<View>(R.id.layout_bottom_sheet)
        val sheetBehavior = BottomSheetBehavior.from(bottomSheet)
        val handleIv = view.findViewById<ImageView>(R.id.iv_sheet_handle)

        // 自定义Drawable实时绘制当前形态的手柄
        val handleDrawable = object : Drawable() {
            private val drawPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
                color = ContextCompat.getColor(requireContext(), R.color.design_handle_color)
                style = Paint.Style.STROKE
                strokeWidth = resources.getDimension(R.dimen.handle_stroke_width)
                strokeCap = Paint.Cap.ROUND
                strokeJoin = Paint.Join.ROUND
            }

            override fun draw(canvas: Canvas) {
                // 适配ImageView的padding和缩放
                val scale = bounds.width() / viewportSize
                canvas.save()
                canvas.scale(scale, scale)
                canvas.drawPath(currentHandlePath, drawPaint)
                canvas.restore()
            }

            override fun setAlpha(alpha: Int) {
                drawPaint.alpha = alpha
                invalidateSelf()
            }

            override fun setColorFilter(colorFilter: ColorFilter?) {
                drawPaint.colorFilter = colorFilter
                invalidateSelf()
            }

            override fun getOpacity() = PixelFormat.TRANSLUCENT
        }
        handleIv.setImageDrawable(handleDrawable)

        // 绑定滑动回调
        sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                // 业务中BottomSheet不会进入HIDDEN状态,无需额外处理隐藏逻辑
            }

            override fun onSlide(bottomSheet: View, slideOffset: Float) {
                // 钳制偏移量在0~1区间,避免越界
                val progress = slideOffset.coerceIn(0f, 1f)
                // 可选:加插值器让过渡更自然,不需要可以直接传progress
                // val interpolatedProgress = PathInterpolator(0.2f, 0f, 0.2f, 1f).getInterpolation(progress)
                pathEvaluator.evaluate(progress, collapsedHandlePath, expandedHandlePath, currentHandlePath)
                handleDrawable.invalidateSelf()
            }
        })

        // 初始化默认折叠态
        pathEvaluator.evaluate(0f, collapsedHandlePath, expandedHandlePath, currentHandlePath)
        handleDrawable.invalidateSelf()
    }
}

3. 适配说明

  • 该方案不存在固定动画时长问题,手指滑动多少,手柄形态就对应变化多少,完全和手势联动
  • 纯路径绘制不会出现之前rotationX方案带来的透视视觉差,和设计稿的两个状态完全对齐
  • 如果需要调整手柄的颜色、粗细,直接修改自定义Drawable中Paint的参数即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:30:51