目的
简单复习下自定义View的绘制。 (今天在摸鱼的时候,听着网易云音乐,突然发现播放页面的碟片动画没写过,于是就试着简单写下效果)
View的测量
先看一幅图(从一本书上拍的) 总结上图: 1.当子控件宽高取固定值(如 ??dp)的时候,控件测量的宽高是自己本身。 2.当子控件宽高取(match_parent、wrap_content)的时候,控件测量的宽高是它的父控件宽高
举例验证(wrap_content、match_parent、固定值)
代码
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//测量控件的宽
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//取测量所得宽度的60%
realWidth = (widthSize * 0.6).toInt()
//设置控件的宽高,因为要取个正方形,所以宽高取同一值
setMeasuredDimension(realWidth, realWidth)
Log.i(TAG, "onMeasure: ----------------宽:$widthSize---------高:$heightSize")
}
wrap_content
match_parent
固定值
上代码
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.core.content.ContextCompat
import androidx.core.graphics.withClip
import androidx.core.graphics.withTranslation
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class CdView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), LifecycleObserver {
private val circlePaint = Paint()
private var realWidth = 0
private var radius = 0f
private val path = Path()
private var bitmap: Bitmap? = BitmapFactory.decodeResource(resources,R.drawable.a1)
private val TAG = "${this.javaClass}"
/**
* 创建旋转动画
*/
private val animator by lazy {
ObjectAnimator.ofFloat(this, "rotation", 0f, 360f).apply {
duration = 4000
interpolator = LinearInterpolator()
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
}
}
init {
circlePaint.apply {
color =Color.BLUE
//位图抗锯齿
isFilterBitmap = true
isAntiAlias = true
}
//禁用硬件加速,裁剪画布才能生效
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//测量控件的宽
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//取测量所得宽度的60%
realWidth = (widthSize * 0.6).toInt()
//设置控件的宽高,因为要取个正方形,所以宽高取同一值
setMeasuredDimension(realWidth, realWidth)
Log.i(TAG, "onMeasure: ----------------宽:$widthSize---------高:$heightSize")
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
radius = realWidth / 2f
bitmap = scaleBitmap()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply {
drawAlbumBg()
drawAlbum()
}
}
/**
*画背景
* @receiver Canvas
*/
private fun Canvas.drawAlbumBg() {
//将原点移动至中心位置(原点默认位置是控件的左上角)
withTranslation(radius, radius) {
drawCircle(0f, 0f, radius, circlePaint)
}
}
/**
* 画封面
* @receiver Canvas
*/
private fun Canvas.drawAlbum() {
withTranslation(radius, radius) {
path.addCircle(0f, 0f, radius * 0.95f, Path.Direction.CW)
//裁剪画布
withClip(path) {
bitmap?.let {
drawBitmap(it, -radius, -radius, circlePaint)
}
}
}
}
/**
* 缩放封面图
* @return Bitmap?
*/
private fun scaleBitmap(): Bitmap? {
return bitmap?.let {
Bitmap.createScaledBitmap(it, realWidth, realWidth, true)
}
}
/**
* 设置封面
* @param bitmapAlbum Bitmap
*/
fun setAlbum(bitmapAlbum: Bitmap) {
bitmap = bitmapAlbum
if (realWidth <= 0) return
bitmap = scaleBitmap()
invalidate()
}
/**
* 开始动画
*/
fun play() {
if (animator.isPaused) animator.resume()
if (!animator.isStarted) animator.start()
}
/**
* 暂停动画
*
*/
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun pause() {
animator.pause()
}
/**
* 停止动画
*/
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun stop() {
animator.cancel()
}