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()
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章