月相变化EclipseAnimView
偶然看到月相变化的oading view,当时觉得挺好看的,正好想到可以用paint.setxfermode 设置 ProterDuffMode SrcOut模式实现。
实现分析
- 根据效果图可以看到,月相有两个周期变化,右偏 从0 到满月 ,左偏从 0 到满月 所以需要通过动画实现 就需要用到ValueAnimator
2.弯月的实现有两种方法
第一种 通过画两个圆 设置后一个圆paint.xfermode 为 SRC_OUT 可以实现两个园混合模式 镂空,既第一个园src 第二个圆 dst ,相交部分留下第一个园部分,同时第二个圆需要 设置透明色
第二种 此种方式通过 drawArc 实现具体实现不表 略复杂
具体实现
- 疑难点,直接在onDraw() 用canvas 实现会有问题,出现第二个圆部分为黑色 解决方式 创建一个canvas 作为新的画布实现 具体看代码
circleBitmap?.recycle()
if (measuredWidth != 0 && measuredHeight != 0) {
circleBitmap = Bitmap.createBitmap(measuredWidth,measuredHeight,Bitmap.Config.ARGB_8888)
circleCanvas = Canvas(circleBitmap)
}
2 。动画执行,这里使用属性动画实现[0,1] ,[0,-1] 两段取值 使用同一动画 在onRepaetEnd 回调中 设置动画执行次数 count++ 在绘制时根据奇偶 设置 if(count % 2 == 0)fraction else -fraction)
if (startAnimator == null) {
startAnimator = ValueAnimator.ofFloat(0f,1f).apply {
repeatMode = ValueAnimator.RESTART
repeatCount = 2
duration = 2000
addUpdateListener {
fraction = it.animatedValue as Float
postInvalidateOnAnimation()
}
addListener(object : AnimatorListenerAdapter(){
override fun onAnimationEnd(animation: Animator?) {
eclipseEnd = true
fraction = 0f
ValueAnimator.ofFloat(0f,4f).apply {
duration = 1000
repeatCount = 1
repeatMode = ValueAnimator.RESTART
addUpdateListener({
fraction = it.animatedValue as Float
postInvalidateOnAnimation()
})
start()
}
}
override fun onAnimationRepeat(animation: Animator?) {
super.onAnimationRepeat(animation)
count ++
Log.d("EclipseAnimView : " ,"count :${count}")
}
})
start()
}
}
在onAttachWindow 中 开启动画 onDetachWindow 中取消动画 以及做释放操作
- 具体绘制看代码 集体实现注意在新的画布中 实现 需要主要 调用canvas.translate 前后最好 canvas.save() canvsa.restore() 还有paint 的前后绘制 属性比如颜色的设置 要做 以及 paint.setxfermode 后绘制完成 paintsetxfermode(null)
view 的画布直接 canvas.drawbitmap(0,0,bitmap,null)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawARGB(255,23,12,53)
if (eclipseEnd) {
canvas.save()
mPaint.color = Color.YELLOW
mPaint.style = Paint.Style.STROKE
canvas.translate(mWidth/2f,mHeight/2f)
val rectf = RectF(-radius,-radius,radius,radius)
canvas.drawArc(rectf,-90 * (1 + fraction),90f ,false,mPaint)
if (fraction >= 4) {
mPaint.style = Paint.Style.FILL
canvas.drawCircle(rectf.centerX(),rectf.centerY(),radius,mPaint)
}
canvas.restore()
} else {
circleCanvas?.let {
it.save()
it.drawARGB(255,23,12,53)
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
it.drawPaint(mPaint)
mPaint.xfermode = null
it.translate(mWidth / 2f , mHeight / 2f)
mPaint.color = Color.YELLOW
it.drawCircle(0f,0f,radius,mPaint)
mPaint.color = Color.TRANSPARENT
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
Log.d("EclipseAnimView : " ,"fraction :${if(count % 2 == 0)fraction else -fraction}")
it.drawCircle((if(count % 2 == 0)fraction else -fraction) * radius * 2,0f,radius,mPaint)
mPaint.xfermode = null
it.restore()
canvas.drawBitmap(circleBitmap,0f,0f,null)
}
}
github地址 ⤧ Next post GestureRulerView GestureDetector与View触摸事件 ⤧ Previous post ParallaxPageTransfomer视差与旋转