Drawable具有轻量级的、高效性、复用性强的特点,相对于View来说,自定义Drawable更加简单,它不用与用户进行交互和点击事件处理。 自定义动画Drawable只要继承Drawable并实现以下4个方法,和同时实现Animatable接口里的方法。
kotlin
class CircleDrawable : Drawable(), Animatable {
private var mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mValueAnimator: ValueAnimator? = null
private var mRadius: Float = 0f //扩散半径
companion object {
const val STROKE_WIDTH = 5f
const val DEFAULT_DURATION = 1200L
const val DEFAULT_DELAY_TIME = 0L
}
//绘制的矩形框
private var mRect = RectF()
//动画启动延迟时间
private var mStartDelay: Long = DEFAULT_DELAY_TIME
private var mRadiusProperty: Property<CircleDrawable, Int> =
object : Property<CircleDrawable, Int>(
Int::class.java, "radius"
) {
override fun set(circleDrawable: CircleDrawable, value: Int) {
circleDrawable.setRadius(value)
}
override fun get(circleDrawable: CircleDrawable): Int {
return circleDrawable.getRadius().toInt()
}
}
fun getRadius(): Float {
return mRadius
}
fun setRadius(radius: Int) {
mRadius = radius.toFloat()
}
init {
mPaint.color = Color.WHITE
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = STROKE_WIDTH
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
mRect.set(clipSquare(bounds))
if (isRunning) {
stop()
}
val maxRadius = ((mRect.right - mRect.left) / 2).toInt()
val radiusHolder = PropertyValuesHolder.ofInt(mRadiusProperty, 0, maxRadius)
val alphaHolder = PropertyValuesHolder.ofInt("alpha", 255, 0)
mValueAnimator = ObjectAnimator.ofPropertyValuesHolder(this, radiusHolder, alphaHolder)
mValueAnimator?.startDelay = mStartDelay
mValueAnimator?.duration = DEFAULT_DURATION
mValueAnimator?.addUpdateListener {
invalidateSelf()
}
mValueAnimator?.repeatMode = ValueAnimator.RESTART
mValueAnimator?.repeatCount = ValueAnimator.INFINITE
start()
}
/**
* 裁剪为正方形
*/
private fun clipSquare(bounds: Rect): Rect {
val min = min(bounds.width(), bounds.height())
val cx = bounds.centerX()
val cy = bounds.centerY()
val r = min / 2
return Rect(cx - r, cy - r, cx + r, cy + r)
}
override fun draw(canvas: Canvas) {
canvas.drawCircle(mRect.centerX(), mRect.centerY(), mRadius, mPaint)
}
override fun setAlpha(alpha: Int) {
mPaint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
mPaint.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return PixelFormat.UNKNOWN
}
override fun start() {
mValueAnimator?.start()
}
override fun stop() {
mValueAnimator?.end()
}
override fun isRunning(): Boolean {
return mValueAnimator?.isRunning == true
}
}
代码流程大概,在构造函数里进行画笔的初始化,重写了onBoundsChanged(bounds:Rect)来获取图形边框参数(这个方法和View#onSizeChaned方法类似),是Drawable的Rect的4个参数值改变时候回调的。当Drawable设置给ImageView时,这个方法就能获取到ImageView的大小,在我们进行动画执行,需要对这个Rect进行裁剪一下(有可能不是正方形),后面还自定义了一个扩散半径的属性mRadiusProperty用来控制绘制圆圈的半径,
kotlin
private var mRadiusProperty: Property<CircleDrawable, Int> =
object : Property<CircleDrawable, Int>(
Int::class.java, "radius"
) {
override fun set(circleDrawable: CircleDrawable, value: Int) {
circleDrawable.setRadius(value)
}
override fun get(circleDrawable: CircleDrawable): Int {
return circleDrawable.getRadius().toInt()
}
}
fun getRadius(): Float {
return mRadius
}
fun setRadius(radius: Int) {
mRadius = radius.toFloat()
}
我们View源码中像alpha,translationX,等

使用也比较简单:
ini
<ImageView
android:id="@+id/image"
android:layout_width="200dp"
android:layout_height="100dp"
android:background="@color/purple_200" />
ini
val imageview = findViewById<ImageView>(R.id.image)
imageview.setImageDrawable(CircleDrawable())
这样就可以看到跟随半径变大,透明度变化的圆圈。