Android自定义LoadingView
好久没更新博客了,过年回来没什么事,把之前的写的东西记录一下吧~
之前因为公司项目需求只要自定义一个loading,效果如下–
没错,就是一个由几个小圆组成的一个转动的大圆,小圆会根据转动不断变小。
好了不多说废话,接下来我们看下怎么实现改效果~
/**
* 内部类,小圆的参数
*/
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初始值为2000(2秒),根据频率计算出来增量的次数
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)
}
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()
}