偶然看见别人写了一个抽奖的转盘控件,赶脚还不错,看样子觉得不是很难,可以一试
、
别人写的是上面这个效果的,我也试着写了一个,下面这个效果的,大概意思是差不多
先来看看思路,首先实现这个效果肯定得先画内部的分区和文字
然后再画外边缘和白点
最后画中间的指针和文字
其实很简单
总结下来就下面结果方法
好了,话不多说,贴代码吧,写的挺清晰地
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)
}
}