效果图:
代码如下:
/**
* @CreateDate: 2021/1/28 11:32
* @Author: ZZJ
* @Description: pk匹配View
*/
class PkMatchingView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
/**
* 画笔
* */
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
}
/**
*中间球形大小 默认364像素
* */
private var mSphereSize = 182f.px
/**
*小弧形半径
* */
private var smallRadius = 0f
/**
*大弧形半径
* */
private var largeRadius = 0f
/**
* 中间球形到小弧形的距离 默认60f
* */
private var smallDistance = 100f
/**
* 大弧形到小弧形的距离 默认60f
* */
private var largeDistance = 80f
/**
* 弧线宽度
* */
private var arcWide = 12f
/**
* 小弧线长度(对应角度)
* */
private var smallLength = 90f
/**
* 大弧线长度(对应角度)
* */
private var largeLength = 90f
/**
* 圆形大小
* */
private var circleSize = 40f
/**
* 圆形颜色
* */
private var circleColor = Color.parseColor("#47CBDE")
/**
* 渐变颜色集(有透明度)
* */
private val mColors = intArrayOf(
Color.parseColor("#4c92f0fe"),
Color.parseColor("#9925e2fd"),
Color.parseColor("#1992f0fe"),
Color.parseColor("#9925e2fd"),
Color.parseColor("#1992f0fe"),
Color.parseColor("#9925e2fd"),
Color.parseColor("#4c92f0fe")
)
/**
* 小弧形区域
* */
private val smallRectF = RectF()
/**
* 大弧形区域
* */
private val largeRectF = RectF()
/**
* 小弧形圆点增值
* */
private var smallValue = 0.0
/**
* 小弧形圆点增值速度
* */
private var smallSpeed = 0.3
/**
* 用于保存小弧圆点角度
* */
private var smallDegrees = 0.0
/**
* 小弧形圆点是否相反移动
* */
private var smallContrary = true
/**
* 大弧形圆点增值
* */
private var largeValue = 0.0
/**
* 小弧形圆点增值速度
* */
private var largeSpeed = 0.25
/**
* 用于保存大弧圆点角度
* */
private var largeDegrees = 0.0
/**
* 大弧形圆点是否相反移动
* */
private var largeContrary = true
/**
* 渐变色Shader
* */
private var mShader: Shader? = null
/**
* view的最小宽高
* */
private var miniSize =
(mSphereSize + (smallDistance + largeDistance + arcWide) * 2 + circleSize / 2).toInt()
/**
* 旋转角度
* */
@SuppressLint("AnimatorKeep")
var rotationAngle = 0f
set(value) {
field = value
invalidate()
}
/**
* 小弧形开始角度
* */
@SuppressLint("AnimatorKeep")
var smallStartAngle = 225f
set(value) {
field = value
invalidate()
}
/**
* 大弧形开始角度
* */
@SuppressLint("AnimatorKeep")
var largeStartAngle = 225f
set(value) {
field = value
invalidate()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = resolveSize(miniSize, widthMeasureSpec)
val height = resolveSize(miniSize, heightMeasureSpec)
setMeasuredDimension(min(width, height), min(width, height))
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//根据控件宽高 重新校对值(中间球大小取决于弧形边距)
if (miniSize > width) {
mSphereSize = ((width - circleSize) / 2 - smallDistance - largeDistance) * 2
}
smallRadius = smallDistance + (mSphereSize / 2)
largeRadius = smallRadius + largeDistance
smallRectF.set(
width / 2f - smallRadius,
height / 2f - smallRadius,
width / 2f + smallRadius,
height / 2f + smallRadius
)
largeRectF.set(
width / 2f - largeRadius,
height / 2f - largeRadius,
width / 2f + largeRadius,
height / 2f + largeRadius
)
mShader = SweepGradient(width / 2f, height / 2f, mColors, null)
val rotationAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", 0f, 360f)
rotationAnimator.repeatMode = ObjectAnimator.RESTART//匀速
rotationAnimator.repeatCount = ValueAnimator.INFINITE//永久循环
val radianAnimator1 = ObjectAnimator.ofFloat(this, "smallStartAngle", 360f, 0f)
radianAnimator1.repeatMode = ObjectAnimator.RESTART//匀速
radianAnimator1.repeatCount = ValueAnimator.INFINITE//永久循环
val radianAnimator2 = ObjectAnimator.ofFloat(this, "largeStartAngle", 80f, 440f)
radianAnimator2.repeatMode = ObjectAnimator.RESTART//匀速
radianAnimator2.repeatCount = ValueAnimator.INFINITE//永久循环
val animatorSet = AnimatorSet()
animatorSet.playTogether(rotationAnimator, radianAnimator1, radianAnimator2)
animatorSet.duration = 4000
animatorSet.interpolator = LinearInterpolator()
animatorSet.start()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//画中间球
drawRotateBitmap(
canvas,
paint,
getAvatar(mSphereSize),
rotationAngle,
(width - mSphereSize) / 2,
(height - mSphereSize) / 2
)
//画圆弧
paint.shader = mShader
paint.strokeWidth = arcWide
canvas.drawArc(smallRectF, smallStartAngle, 90f, false, paint)
canvas.drawArc(smallRectF, smallStartAngle + 90f + 90f, 90f, false, paint)
canvas.drawArc(largeRectF, largeStartAngle, 90f, false, paint)
canvas.drawArc(largeRectF, largeStartAngle + 90f + 90f, 90f, false, paint)
//画圆点
paint.strokeWidth = circleSize
paint.shader = null
paint.color = circleColor
//画小弧点
//degrees 225..315
smallDegrees = smallStartAngle + smallValue
if (smallDegrees >= (smallStartAngle + smallLength)) {
smallContrary = false
} else if (smallDegrees <= smallStartAngle) {
smallContrary = true
}
if (smallContrary) {
smallValue += smallSpeed
} else {
smallValue -= smallSpeed
}
paint.strokeWidth = circleSize - 5
var radians1 = Math.toRadians(smallDegrees)
canvas.drawPoint(
(width / 2f + cos(radians1) * smallRadius).toFloat(),
(height / 2f + sin(radians1) * smallRadius).toFloat(),
paint
)
//degrees 405..495
paint.strokeWidth = circleSize
radians1 = Math.toRadians((smallStartAngle + 180 + smallValue))
canvas.drawPoint(
(width / 2f + cos(radians1) * smallRadius).toFloat(),
(height / 2f + sin(radians1) * smallRadius).toFloat(),
paint
)
//画大弧点
//degrees 125..215
largeDegrees = largeStartAngle + largeValue
if (largeDegrees >= (largeStartAngle + largeLength)) {
largeContrary = false
} else if (largeDegrees <= largeStartAngle) {
largeContrary = true
}
if (largeContrary) {
largeValue += largeSpeed
} else {
largeValue -= largeSpeed
}
paint.strokeWidth = circleSize - 5
var degrees2 = largeStartAngle + largeValue
var radians2 = Math.toRadians(degrees2)
canvas.drawPoint(
(width / 2f + cos(radians2) * largeRadius).toFloat(),
(height / 2f + sin(radians2) * largeRadius).toFloat(),
paint
)
//degrees 305..395
paint.strokeWidth = circleSize
degrees2 = largeStartAngle + 180 + largeValue
radians2 = Math.toRadians(degrees2)
canvas.drawPoint(
(width / 2f + cos(radians2) * largeRadius).toFloat(),
(height / 2f + sin(radians2) * largeRadius).toFloat(),
paint
)
}
private fun drawRotateBitmap(
canvas: Canvas, paint: Paint, bitmap: Bitmap,
rotation: Float, posX: Float, posY: Float
) {
val matrix = Matrix()
val offsetX = bitmap.width / 2f
val offsetY = bitmap.height / 2f
matrix.postTranslate(-offsetX, -offsetY)
matrix.postRotate(rotation)
matrix.postTranslate(posX + offsetX, posY + offsetY)
canvas.drawBitmap(bitmap, matrix, paint)
}
//获取指定大小的Bitmap
private fun getAvatar(width: Float): Bitmap {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(resources, R.drawable.ic_pk_matching, options)
options.inJustDecodeBounds = false
options.inDensity = options.outWidth
options.inTargetDensity = width.toInt()
return BitmapFactory.decodeResource(resources, R.drawable.ic_pk_matching, options)
}
}