Android--大转盘

4 篇文章 0 订阅
3 篇文章 0 订阅

前言

无论是线上推广,还是线下营销,转盘游戏都是非常吸引人的一项活动,就像电视剧《赘婿》中的“拼刀刀”,达到的是万人空巷的影响力。其实,这是一种赌徒心理。每个人都希望不劳而获,这是赌徒心理的成长的肥沃土壤,不幸的是,千百万年的成长中,人已经形成了避难趋易的本性。转盘是一种傻瓜式的操作,只需要手指点一下,不过几秒钟,就可以收到反馈。而想要立即得到结果,害怕未知依然是每个人的本质。所以,转盘游戏有其滋生的土壤。
转盘效果

思考

怎么样实现一个转盘那?首先:转盘是一个圆,然后把圆等分成几份,每一份都是一个扇形区域,在扇形区域中画出我们先展示的内容,接着,中间可能有装饰(图片),最后,按照层级一层层画。
想必大家都对自定义View的过程了熟于心,这里就不过多介绍了。下面我们进入代码。

代码

  1. 自定义转盘
    这里没有给出全部的代码,而是抽取了一些关键步骤,文章的最后有给出全部代码。
class LuckySpinView : View {
    constructor(context: Context?) : super(context) {
        initPaint()
    }
    // 初始化画笔。自定义View前先想好需要哪些画笔,对实现View有神奇效果
    private fun initPaint() {
        mArcPaint = Paint()
        mArcPaint.isAntiAlias = true
        mArcPaint.isDither = true

        mTextPaint = TextPaint()
        mTextPaint.style = Paint.Style.FILL
        mTextPaint.isAntiAlias = true
        mTextPaint.isDither = true
        mTextPaint.color = mTextColor
        mTextPaint.textSize = mTextSize
        mTextPaint.typeface = ResourcesCompat.getFont(context, R.font.impact)

        mTextStrokePaint = TextPaint(mTextPaint)
        mTextStrokePaint.style = Paint.Style.STROKE
        mTextStrokePaint.strokeWidth = TEXT_STROKE_WIDTH

        mBackgroundPaint = Paint()
        mBackgroundPaint.isAntiAlias = true
        mBackgroundPaint.isDither = true
    }
	// 测量阶段。自定义View必要的阶段,用来规范我们View的展示大小
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val width = measuredWidth.coerceAtMost(measuredHeight)
        mPadding = paddingLeft

        if (mOutRadius == 0) mOutRadius = width / 2
        if (mInnerRadius == 0) mInnerRadius = width / 2
        setMeasuredDimension(mOutRadius * 2, mOutRadius * 2)
    }
	// 绘画阶段。自定义View必要的阶段,画布承载我们想要的效果,画笔将我们的想法变成现实
    /**
     * @param canvas
     */
    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawArc(canvas) // 画扇形区域
        drawCenterImg(canvas) //画中间的区域
        drawOutBoundImg(canvas) // 画外围的装饰
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    private fun drawCenterImg(canvas: Canvas) {
        if (mCenterImageId == 0) return
        if (mCenterBitmap == null) {
            val drawable = resources.getDrawable(mCenterImageId, null)
            val bitmap = Utils.drawableToBitmap(drawable)
            mCenterBitmap = Bitmap.createScaledBitmap(
                bitmap,
                drawable!!.intrinsicWidth,
                drawable.intrinsicHeight,
                false
            )
        }
        val bitmap = mCenterBitmap!!
        canvas.drawBitmap(
            bitmap, mCenterPoint.x - bitmap.width / 2.toFloat(),
            mCenterPoint.y - bitmap.height / 2.toFloat(), null
        )
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    private fun drawOutBoundImg(canvas: Canvas) {
        val drawable = resources.getDrawable(mBackgroundImgId, null)
        drawable.bounds = Rect(
            mOutRange.left.toInt() ,
            mOutRange.top.toInt() ,
            mOutRange.right.toInt() ,
            mOutRange.bottom.toInt()
        )
        drawable.draw(canvas)
    }
    private fun drawArc(canvas: Canvas) {
        var tmpAngle = mStartAngle
        val sweepAngle = 360f / mRewardList.size

        for (i in mRewardList.indices) {
            fillArc(canvas, tmpAngle, sweepAngle, i)
            drawArcBorder(canvas, tmpAngle, sweepAngle)
            drawItemContentWithoutIcon(i, canvas, tmpAngle, sweepAngle)
            tmpAngle += sweepAngle
        }
    }

    private fun rotateTo(index: Int) {
        HandlerUtils.runOnUiThread {
            val rand = Random()
            rotateTo(index, rand.nextInt() and 1, true)
        }
    }
    // 执行旋转
    fun rotateTo(index: Int, rotation: Int, startShow: Boolean) {
        if (isRunning) {
            return
        }
        val rotationAssess = if (rotation <= 0) 1 else -1
        var roundOfNumber = mRoundOfNumber
        val accDuration = 1500L
        var decDuration = 1500L
        var interpolator: TimeInterpolator

        // 从上一次转盘最终的位置处旋转
        if (getRotation() != 0.0f) {
            setRotation(getRotation() % 360f)
            interpolator = AccelerateInterpolator()
            animate()
                .setInterpolator(interpolator)
                .setDuration(accDuration)
                .setListener(object : Animator.AnimatorListener {
                    override fun onAnimationStart(animation: Animator) {
                        isRunning = true
                    }

                    override fun onAnimationEnd(animation: Animator) {
                        isRunning = false
                        setRotation(0f) // 加速到角度为0的水平轴, 接着再减速到目标角度
                        rotateTo(index, rotation, false)
                    }

                    override fun onAnimationCancel(animation: Animator) {}

                    override fun onAnimationRepeat(animation: Animator) {}
                })
                .rotation(360f * roundOfNumber * rotationAssess)
                .start()
            return
        }
        interpolator = DecelerateInterpolator()

        // 进入转盘后的第一次玩转盘, 先加速再减速
        if (startShow) {
            roundOfNumber = roundOfNumber shl 1
            decDuration = accDuration shl 1
            interpolator = AccelerateDecelerateInterpolator()
        }

        val targetAngle =
            360f * roundOfNumber * rotationAssess + 270f - getAngleOfIndexTarget(index) - 360f / mRewardList.size / 2
        animate()
            .setInterpolator(interpolator)
            .setDuration(decDuration + 500L)
            .setListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {
                    isRunning = true
                }

                override fun onAnimationEnd(animation: Animator) {
                    isRunning = false
                    setRotation(getRotation() % 360f)
                    if (mRotateListener != null) {
                        mRotateListener!!.rotateDone(index)
                    }
                }

                override fun onAnimationCancel(animation: Animator) {}
                override fun onAnimationRepeat(animation: Animator) {}
            })
            .rotation(targetAngle)
            .start()
    }

    fun setTargetIndex(index: Int) {
        targetIndex = index
    }

	// 开始旋转
    fun startLuckyWheelWithTargetIndex(index: Int) {
        this.rotateTo(index)
    }

    fun startLuckyWheelWithRandomTarget() {
        val r = Random()
        this.rotateTo(r.nextInt(this.rewardListSize - 1))
    }
	// 取消旋转
    fun cancelRotate() {
        animate().cancel()
    }
}

这里有全部的代码,欢迎查看,LuckySpin

总结

在实现转盘游戏的过程中,总会碰到一些拦路虎。如果避其锋芒,不敢与之对抗,那么此消彼长,它会一步步成长,直到你觉得不能再逃避想要面对它时,它已经是个庞然大物,此时便会有心无力。碰到困难,最简单也是最有效的方法,直接面对它,将它分解成你能解决的许多小块,这就是分治算法的核心:分而治之。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值