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

Kotlin实现RotateAnimation转盘动画点击spinBtn无响应问题求助

核心问题定位

  • 控件初始化顺序错误,点击事件未成功绑定
  • 随机数范围错误存在数组越界风险
  • ViewModel越权处理视图层动画逻辑,不符合架构规范

修复方案

1. 修正GameFragment的视图初始化逻辑

你已经引入了ViewBinding,直接通过binding对象访问控件即可,不需要手动调用findViewById,同时调整初始化顺序,先inflate得到binding对象再配置控件逻辑:

class GameFragment : Fragment() {

    private lateinit var gameViewModel: GameViewModel
    private var _binding: GameFragmentBinding? = null
    // 仅在onCreateView和onDestroyView之间有效
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // 先 inflate 得到视图实例
        _binding = GameFragmentBinding.inflate(inflater, container, false)
        gameViewModel = ViewModelProvider(this).get(GameViewModel::class.java)
        gameViewModel.getDegreesForSectors()
        
        // 直接通过binding访问控件,绑定点击事件
        binding.spinBtn.setOnClickListener {
            if (!gameViewModel.isSpinning) {
                spinWheel()
            }
        }
        return binding.root
    }

    // 动画属于视图层逻辑,放在Fragment中实现
    private fun spinWheel() {
        gameViewModel.isSpinning = true
        val targetDegree = gameViewModel.getRandomTargetDegree()
        val rotateAnimation = RotateAnimation(
            0f,
            (360 * gameViewModel.fieldCount + targetDegree).toFloat(),
            RotateAnimation.RELATIVE_TO_SELF, 0.5f,
            RotateAnimation.RELATIVE_TO_SELF, 0.5f
        ).apply {
            duration = 3600
            fillAfter = true
            interpolator = DecelerateInterpolator()
            setAnimationListener(object : Animation.AnimationListener {
                override fun onAnimationStart(animation: Animation) {}
                override fun onAnimationEnd(animation: Animation) {
                    gameViewModel.isSpinning = false
                    // 此处可添加旋转结束后的业务逻辑,比如读取中奖结果
                }
                override fun onAnimationRepeat(animation: Animation) {}
            })
        }
        // 注意和你的布局id对应,ViewBinding会自动把下划线转为驼峰命名
        binding.spinningWheel.startAnimation(rotateAnimation)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

2. 修正GameViewModel的逻辑,仅保留业务数据计算

class GameViewModel : ViewModel() {
    private val fields = arrayOf("1","2","3","4","5","6","7")
    private val fieldDegrees = IntArray(fields.size)
    var isSpinning = false
    // 对外暴露扇区数量供动画计算使用
    val fieldCount get() = fields.size

    fun getDegreesForSectors(){
        val oneFieldDegree = 360 / fields.size
        for (i in fields.indices){
            fieldDegrees[i] = (i+1) * oneFieldDegree
        }
    }

    // 修正随机数范围为0至fields.size-1,避免数组越界
    fun getRandomTargetDegree(): Int {
        val randomIndex = (0 until fields.size).random()
        return fieldDegrees[randomIndex]
    }
}

可选优化提示

如果后续需要支持不等分扇区,仅需修改getDegreesForSectors中的角度分配逻辑即可,无需改动视图层的动画代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:36:03