自定义View之抽奖转盘

偶然看见别人写了一个抽奖的转盘控件,赶脚还不错,看样子觉得不是很难,可以一试
这里写图片描述
别人写的是上面这个效果的,我也试着写了一个,下面这个效果的,大概意思是差不多
这里写图片描述

先来看看思路,首先实现这个效果肯定得先画内部的分区和文字
然后再画外边缘和白点
最后画中间的指针和文字
其实很简单
总结下来就下面结果方法
这里写图片描述
好了,话不多说,贴代码吧,写的挺清晰地

class LuckyDrawView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    companion object {
        val PART = 6
    }

    //开始角度
    private var startAngle = 0f
    //平均每份的角度
    private val averageAngle = 360f / (PART + 1)
    // 宽高
    private var mWidth: Int = 0
    private var mHeight: Int = 0
    //中心的坐标
    private var mCenterX: Int = 0
    private var mCenterY: Int = 0
    //画内部分区的画笔
    private val mPaintInnerBg = Paint(Paint.ANTI_ALIAS_FLAG)
    //画文字的画笔
    private val mPaintText = Paint(Paint.ANTI_ALIAS_FLAG)
    //画外部圆弧的画笔
    private val mPaintOutBg = Paint(Paint.ANTI_ALIAS_FLAG)
    //内部的转盘
    private val mPaintPoint = Paint(Paint.ANTI_ALIAS_FLAG)
    //中间文字的画笔
    private val mPaintTextInner = Paint(Paint.ANTI_ALIAS_FLAG)
    //画转盘头的画笔
    private val mPaintPath = Paint(Paint.ANTI_ALIAS_FLAG)
    private val padding = dp2px(20f)
    private val colors = intArrayOf(Color.parseColor("#FFBBFF")
            , Color.parseColor("#FFAEB9"), Color.parseColor("#FAF0E6")
            , Color.parseColor("#D1EEEE"), Color.parseColor("#FAEBD7")
            , Color.parseColor("#FFAEB9"), Color.parseColor("#FAF0E6")
    )
    private val strArras = arrayOf<String>("10元", "20元", "30元", "50元", "100元", "200元", "谢谢惠顾")
    private var bitmap: Bitmap
    private var tanksBitmap: Bitmap

    //初始化
    init {
        mPaintInnerBg.style = Paint.Style.FILL

        mPaintText.textSize = sp2px(20f)
        mPaintText.style = Paint.Style.STROKE
        mPaintText.color = Color.parseColor("#9400D3")

        mPaintOutBg.style = Paint.Style.FILL
        mPaintOutBg.color = Color.parseColor("#EE5C42")

        mPaintPoint.style = Paint.Style.FILL
        mPaintPoint.color = Color.parseColor("#FFA500")

        mPaintPath.style = Paint.Style.FILL
        mPaintPath.color = Color.parseColor("#FFA500")


        mPaintTextInner.textSize = sp2px(22f)
        mPaintTextInner.style = Paint.Style.STROKE
        mPaintTextInner.color = Color.WHITE
        mPaintTextInner.textAlign = Paint.Align.CENTER


        bitmap = decodeResource(resources, R.mipmap.money)
        tanksBitmap = decodeResource(resources, R.mipmap.thanks)

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = measureWidth(widthMeasureSpec)
        val height = measureHeight(heightMeasureSpec)
        val imageSize = if (width < height) width else height
        setMeasuredDimension(imageSize, imageSize)

    }

    /**
     * 测量宽度
     */
    private fun measureWidth(measureSpec: Int): Int {
        val result: Int
        val specMode = View.MeasureSpec.getMode(measureSpec)
        val specSize = View.MeasureSpec.getSize(measureSpec)

        if (specMode == View.MeasureSpec.EXACTLY) {
            result = specSize
        } else if (specMode == View.MeasureSpec.AT_MOST) {
            result = specSize
        } else {
            result = specSize
        }
        return result
    }

    /**
     * 测量高度
     */
    private fun measureHeight(measureSpecHeight: Int): Int {
        val result: Int
        val specMode = View.MeasureSpec.getMode(measureSpecHeight)
        val specSize = View.MeasureSpec.getSize(measureSpecHeight)

        if (specMode == View.MeasureSpec.EXACTLY) {
            result = specSize
        } else if (specMode == View.MeasureSpec.AT_MOST) {
            result = specSize
        } else {
            result = specSize
        }
        return result
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth = w;
        mHeight = h;
        mCenterX = mWidth / 2
        mCenterY = mHeight / 2
    }

    override fun onDraw(canvas: Canvas) {
        canvas.translate(mCenterX.toFloat(), mCenterY.toFloat());
        drawOutBg(canvas)
        drawInnerBg(canvas)
        drawText(canvas)
        drawPicture(canvas)
        drawCenter(canvas)
    }

    public fun startAnimator() {
        startAngle = 0f
        //动画时长
        val animatorDuration = 4000
        //随机角度
        val randomAngle = Random().nextInt(360)
        val valueAnimator = ValueAnimator.ofFloat(0f, -(360 * 4 + randomAngle + 90).toFloat()).setDuration(animatorDuration.toLong())
        valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
            override fun onAnimationUpdate(animation: ValueAnimator) {
                startAngle = animation.getAnimatedValue() as Float
                invalidate()

            }

        })
        valueAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                when {
                    randomAngle in 0..averageAngle.toInt() -> ToastBottomUtils.showToast("您中了10元")
                    randomAngle in averageAngle.toInt()..2 * averageAngle.toInt() -> ToastBottomUtils.showToast("您中了20元")
                    randomAngle in 2 * averageAngle.toInt()..3 * averageAngle.toInt() -> ToastBottomUtils.showToast("您中了30元")
                    randomAngle in 3 * averageAngle.toInt()..4 * averageAngle.toInt() -> ToastBottomUtils.showToast("您中了50元")
                    randomAngle in 4 * averageAngle.toInt()..5 * averageAngle.toInt() -> ToastBottomUtils.showToast("您中了100元")
                    randomAngle in 5 * averageAngle.toInt()..6 * averageAngle.toInt() -> ToastBottomUtils.showToast("您中了200元")
                    randomAngle in 6 * averageAngle.toInt()..7 * averageAngle.toInt() -> ToastBottomUtils.showToast("sorry,您没有中奖")
                }
            }
        })
        valueAnimator.start()

    }

    private fun drawOutBg(canvas: Canvas) {
        mPaintOutBg.color = Color.parseColor("#EE5C42")
        canvas.drawCircle(0f, 0f, mCenterX.toFloat(), mPaintOutBg)
        var beginAngle = startAngle + averageAngle / 2
        mPaintOutBg.color = Color.WHITE
        for (i in 0..PART * 2) {
            canvas.save()
            canvas.rotate(beginAngle)
            canvas.drawCircle(mCenterX - padding / 2, 0f, sp2px(5f), mPaintOutBg)
            beginAngle += averageAngle / 2
            canvas.restore()
        }
    }

   private fun drawCenter(canvas: Canvas) {
        val path = Path()
        path.moveTo(-mCenterX / 8f, 0f)
        path.lineTo(mCenterX / 8f, 0f)
        path.lineTo(0f, -mCenterX / 3f)
        path.close()
        canvas.drawPath(path, mPaintPath)
        val fm = mPaintTextInner.getFontMetrics()
        val mTxtHeight = Math.ceil((fm.leading - fm.ascent).toDouble()).toFloat()
        canvas.drawCircle(0f, 0f, mCenterX / 6f, mPaintPoint)
        canvas.drawText("GO", 0f, mTxtHeight/2, mPaintTextInner)
    }


    private fun drawPicture(canvas: Canvas) {
        val width = bitmap.width
        val height = bitmap.height
        var beginAngle = startAngle + averageAngle / 2
        for (i in 0..PART) {
            canvas.save()
            canvas.rotate(beginAngle)
            // 指定图片绘制区域
            val src = Rect(0, 0, width, height)
            //指定图片在屏幕上显示的区域
            val dst = Rect(mCenterX * 2 / 5, -height / 2, mCenterX * 2 / 5 + width, height / 2)
            if (i == PART) {
                canvas.drawBitmap(tanksBitmap, src, dst, null);
            } else {
                canvas.drawBitmap(bitmap, src, dst, null);
            }
            beginAngle += averageAngle
            canvas.restore()
        }
    }

    private fun drawText(canvas: Canvas) {
        for (i in 0..PART) {
            //创建绘制路径
            val circlePath = Path()
            val r = min(mCenterX, mCenterY) - padding
            val rect = RectF(-r, -r, r, r)
            circlePath.addArc(rect, startAngle, averageAngle)
            val measurewidth = mPaintText.measureText(strArras[i])
            val hOffset = (Math.sin(averageAngle / 2 / 180 * Math.PI) * r).toFloat() - measurewidth / 2
            //第三个参数,h方向的偏移量,4个参数外边缘的偏移量
            canvas.drawTextOnPath(strArras[i], circlePath, hOffset, (r / 4).toFloat(), mPaintText);
            startAngle += averageAngle
        }

    }

    private fun drawInnerBg(canvas: Canvas) {

        val r = min(mCenterX, mCenterY) - padding
        val rect = RectF(-r, -r, r, r)
        for (i in 0..PART) {
            mPaintInnerBg.color = colors[i]
            canvas.drawArc(rect, startAngle, averageAngle, true, mPaintInnerBg);
            startAngle += averageAngle
        }

    }


    private fun sp2px(spValue: Float): Float {
        val fontScale = resources.displayMetrics.scaledDensity
        return (spValue * fontScale + 0.5f)
    }

    private fun dp2px(dp: Float): Float {
        val scale = resources.displayMetrics.density
        return (dp * scale + 0.5f)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.getAction()) {
            MotionEvent.ACTION_DOWN -> {
                //获取屏幕上点击的坐标
                val x = event.getX()
                val y = event.getY()
                if (x > width / 3 && x < width * 2 / 3 && y > width / 3 && y < width * 2 / 3) {
                    startAnimator()
                    return true
                }
            }
        }
        //这句话不要修改
        return super.onTouchEvent(event)
    }
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/villa_mou/article/details/78963074