Android自定义CustomWheelView选中结果与显示不符问题求助
转盘控件显示与输出不匹配问题修复
问题描述
开发的CustomWheelView转盘停止时,指针指向的选项与输出结果不一致,例如指针停在Italian选项时,输出却显示“你选择了Indian”。
核心原因
代码同时使用了自定义angle变量控制绘制和View的rotation动画,导致实际显示的旋转角度与代码中用于计算选中项的angle变量不一致;再加上选中索引的计算逻辑存在角度偏移错误,最终引发匹配问题。
修复方案
方案一:使用自定义角度变量控制旋转(推荐)
移除View的rotation动画,改用ValueAnimator更新自定义angle变量,确保绘制和选中项计算使用同一角度源:
- 修改
spinWheel方法(需导入android.animation.Animator和android.animation.ValueAnimator):
fun spinWheel() { if (!isSpinning) { isSpinning = true val totalSpinAngle = (360 * 5 + (0..360).random()).toFloat() val startAngle = angle val endAngle = startAngle + totalSpinAngle ValueAnimator.ofFloat(startAngle, endAngle).apply { duration = 3000 addUpdateListener { animation -> angle = animation.animatedValue as Float invalidate() } addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator) {} override fun onAnimationEnd(animation: Animator) { isSpinning = false // 执行弹跳效果 ValueAnimator.ofFloat(angle, angle + 5f).apply { duration = 100 addUpdateListener { angle = it.animatedValue as Float; invalidate() } addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator) {} override fun onAnimationEnd(animation: Animator) { ValueAnimator.ofFloat(angle, angle - 5f).apply { duration = 100 addUpdateListener { angle = it.animatedValue as Float; invalidate() } addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator) {} override fun onAnimationEnd(animation: Animator) { angle = (angle - 5f) % 360 calculateAndNotifySelectedItem() } override fun onAnimationCancel(animation: Animator) {} override fun onAnimationRepeat(animation: Animator) {} }) start() } } override fun onAnimationCancel(animation: Animator) {} override fun onAnimationRepeat(animation: Animator) {} }) start() } } override fun onAnimationCancel(animation: Animator) {} override fun onAnimationRepeat(animation: Animator) {} }) start() } } } private fun calculateAndNotifySelectedItem() { val sweepAngle = 360f / segments.size val normalizedAngle = (angle % 360 + 360) % 360 // 确保角度为正 val relativeTopAngle = (360 - normalizedAngle) % 360 // 计算指针(顶部)相对于转盘的角度 val selectedIndex = (relativeTopAngle / sweepAngle).toInt() % segments.size onWheelStoppedListener?.invoke(segments[selectedIndex]) }
- 保留原
onDraw方法不变,它已基于angle变量正确绘制转盘。
方案二:使用View的rotation控制旋转
放弃自定义angle变量,直接用View的rotation属性获取当前角度,同步绘制和计算逻辑:
- 修改
onDraw方法:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val width = width.toFloat() val height = height.toFloat() val radius = min(width, height) / 2 rectF.set(width / 2 - radius, height / 2 - radius, width / 2 + radius, height / 2 + radius) val currentAngle = (rotation % 360 + 360) % 360 // 获取当前View的旋转角度并归一化 val sweepAngle = 360f / segments.size for (i in segments.indices) { paint.shader = LinearGradient( rectF.centerX(), rectF.top, rectF.centerX(), rectF.bottom, gradientColors[i % gradientColors.size], gradientColors[(i + 1) % gradientColors.size], Shader.TileMode.CLAMP ) canvas.drawArc(rectF, currentAngle + i * sweepAngle, sweepAngle, true, paint) val textAngle = Math.toRadians((currentAngle + i * sweepAngle + sweepAngle / 2).toDouble()) val x = (width / 2 + radius * 0.65 * Math.cos(textAngle)).toFloat() val y = (height / 2 + radius * 0.65 * Math.sin(textAngle)).toFloat() canvas.drawText(segments[i], x, y, textPaint) } }
- 修改
spinWheel方法中的选中项计算逻辑:
fun spinWheel() { if (!isSpinning) { isSpinning = true val randomAngle = (360 * 5 + (0..360).random()).toFloat() animate() .rotation(randomAngle) .setDuration(3000) .withEndAction { animate().rotationBy(5f).setDuration(100).withEndAction { animate().rotationBy(-5f).setDuration(100).withEndAction { isSpinning = false invalidate() val sweepAngle = 360f / segments.size val currentAngle = (rotation % 360 + 360) % 360 val relativeTopAngle = (360 - currentAngle) % 360 val selectedIndex = (relativeTopAngle / sweepAngle).toInt() % segments.size onWheelStoppedListener?.invoke(segments[selectedIndex]) }.start() }.start() } .start() } }
说明
两种方案都能解决显示与输出不匹配的问题,方案一更灵活,便于后续扩展转盘的自定义逻辑;方案二更贴合View的原生动画机制。
内容的提问来源于stack exchange,提问作者Islam Farrag
相关产品推荐
相关产品推荐

