Android自定义LoadingView

Android自定义LoadingView

好久没更新博客了,过年回来没什么事,把之前的写的东西记录一下吧~
之前因为公司项目需求只要自定义一个loading,效果如下–
这里写图片描述

没错,就是一个由几个小圆组成的一个转动的大圆,小圆会根据转动不断变小。
好了不多说废话,接下来我们看下怎么实现改效果~

  • 首先先思考,这个view是由若干个小圆构成的,每个小圆的直径跟变化都不同步,所以我们要定义一个圆形内部类,方便把每个小圆的对象保存起来
 /**
     * 内部类,小圆的参数
     */
    private inner class CircleWrapper {
         var diameter: Int = 0//圆的直径
         var dynamicDiameter: Float = 0.toFloat()//动态直径
    }

CircleWrapper 类里面有个属性,一个圆的初始化直径diameter,一个是该圆的在某个变化时刻的直径dynamicDiameter

  • 接下来是初始化,首先要初始化画笔,以及小圆每次减少的直径大小
        paint = Paint()
        paint!!.isAntiAlias = true
        paint!!.style = Paint.Style.FILL
        paint!!.color = color
        //一圈周期circleTime初始值为20002秒),根据频率计算出来增量的次数
        number = (circleTime / 2 * 1.0 / 1000 * 83f).toInt().toFloat()
        this.increment = this.smallD / number
        //每次的直径增量
        this.increment = if (this.increment <= 0) 1.0F else this.increment

还有初始化小圆,默认是10个小圆

        var wrappers = ArrayList()  //存储小圆的容器
        val diameter = this.smallD//直径,默认10dp  
        for (i in 10 downTo 1) {
            var wrapper = CircleWrapper()
            wrapper.diameter = diameter
            wrapper.dynamicDiameter = (diameter * i / circleNum).toFloat()   //动态直径递减
            wrappers!!.add(wrapper)
        }
  • 初始化工作完毕,接下来就是测量该view的大小,重写onMeasure()方法
 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthSize = View.MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = View.MeasureSpec.getMode(widthMeasureSpec)
        val heightSize = View.MeasureSpec.getSize(heightMeasureSpec)
        val heightMode = View.MeasureSpec.getMode(heightMeasureSpec)

        if (widthMode == View.MeasureSpec.EXACTLY) {
            mWidth = widthSize
        } else {
            //大圆的直径,加左右两边两个小圆的半径
            mWidth = bigD + smallD  
        }

        if (heightMode == View.MeasureSpec.EXACTLY) {
            mHeight = heightSize
        } else {
            mHeight = bigD + smallD
        }
        setMeasuredDimension(mWidth, mHeight)
    }

-由于不是ViewGroup所有不用layout,所以接下来就是onDraw绘画了
先贴代码

  override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawCircle(canvas)
        invalidate()
    }
 private fun drawCircle(canvas: Canvas) {
        val angle = 360f / circleNum
        for (i in 0 until circleNum) {
            if (wrappers!![i].dynamicDiameter >= 0) {
                wrappers!![i].dynamicDiameter = wrappers!![i].dynamicDiameter - increment
            }
            if (wrappers!![i].dynamicDiameter < 0) {
                wrappers!![i].dynamicDiameter = wrappers!![i].diameter.toFloat()
            }
            //根据角度旋转画布,并且绘制新的圆
            canvas.rotate(-angle, (mWidth / 2).toFloat(), (mHeight / 2).toFloat())
            canvas.drawCircle((mWidth / 2).toFloat(), (mHeight / 2 - bigD / 2).toFloat(), wrappers!![i].dynamicDiameter / 2, paint!!)
        }
    }

代码不多,主要看drawCircle方法,首先根据小圆的数量获得每个小圆之间的角度,这个角度angle下面会用到。
然后遍历10次(有10个小圆),主要到这行没有

 //根据角度旋转画布,并且绘制新的圆
 canvas.rotate(-angle, (mWidth / 2).toFloat(), (mHeight / 2).toFloat())

这步是重点,通过旋转画笔进行绘画,旋转的角度是前面计算出来的角度,然后绘制对应座标的小圆,这样旋转一圈就能把10个小圆根据他们对应的直径绘制出来了!

可是这时怎么让他动起来呢? 加个直径动态缩减就行了

if (wrappers!![i].dynamicDiameter >= 0) {
wrappers!![i].dynamicDiameter = wrappers!![i].dynamicDiameter - increment}
if (wrappers!![i].dynamicDiameter < 0) {
wrappers!![i].dynamicDiameter = wrappers!![i].diameter.toFloat()
}

这几句代码的作用就是当该校园的动态直径大于0时,则直径减少,减少的值前面已经在初始化计算出来了。如果减少到比0小,则恢复到初始化直径大小。

最后调用invalidate() 就能执行动态变化了~

是不是很简单,主要是利用到Canvas对象的rotate方法。然后我们可以自己是加以优化,加一些set属性的方法,这样就能更好的实现动态化拉

 fun setSmallDiameter(d: Int) {
        var d = d
        if (d == 0) d = 40
        this.smallD = d
        initParams()
        invalidate()
    }

    fun setBigDiameter(d: Int) {
        var d = d
        if (d == 0) d = 140
        this.bigD = d
        initParams()
        invalidate()
    }

    fun setCircleTime(time: Int) {
        var time = time
        if (time == 0) time = 2000
        this.circleTime = time
        initParams()
        invalidate()
    }

    fun setCircleNum(num: Int) {
        var num = num
        if (num == 0) num = 10
        this.circleNum = num
        initParams()
        invalidate()
    }

    fun setCircleColor(color: Int) {
        var color = color
        if (color == 0) color = Color.BLUE
        this.color = color
        initParams()
        invalidate()
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章