简单的星球旋转自定义View

效果图:


企业微信截图_1645408447599.png

代码如下:

/**
 * @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)
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容