自定义view之视频播放状态标识


kotlin学习

今天写的东西比较简单,一个自定义view,主要是为了捡捡kotlin语法。废话不多说,先上效果
自定义view之视频播放状态标识
文章图片

【自定义view之视频播放状态标识】这个效果通常在一些播放器上遇到,用来标识视频正在播放中。
根据这个效果我们先明确一下我们需要做的:
1.绘制三条竖线
2.开启动画让它动起来
3.自定义一些属性方便使用和扩展
下面我会直接贴一下代码,代码并不复杂,但是我希望大家带着一些问题去看,比如
为什么这里需要重写onMeasure方法?
canvas在drawLine的时候需要考虑线的宽度吗?
什么时候应该停止动画?
kotlin中init代码块和构造函数中代码的哪个先执行?
自定义view代码:

package com.example.viewimport android.animation.AnimatorSet import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet import android.view.View import com.example.kotlinsturdy.Rclass PlayingView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) {companion object { private const val DEFAULT_WIDTH = 20 private const val DEFAULT_HEIGHT = 60 private const val DEFAULT_LINE_WIDTH = 10 private const val DEFAULT_LINE_HEIGHT = 60 }private val firstLineHeight: Float private val secondLineHeight: Float private val thirdLineHeight: Float private val lineWidth: Float private var firstLineCurrentHeight = 0f private var secondLineCurrentHeight = 0f private var thirdLineCurrentHeight = 0f private lateinit var mPaint: Paint private val lineColor: Int private var animatorSet: AnimatorSet? = null private fun initPaint() { mPaint = Paint(Paint.ANTI_ALIAS_FLAG) mPaint.color = lineColor mPaint.strokeWidth = lineWidth }init { val array = context.obtainStyledAttributes(attrs, R.styleable.PlayingView) firstLineHeight = array.getDimension( R.styleable.PlayingView_firstLineHeight, DEFAULT_LINE_HEIGHT.toFloat() ) firstLineCurrentHeight = firstLineHeight.times(0.3f) secondLineHeight = array.getDimension( R.styleable.PlayingView_secondLineHeight, DEFAULT_LINE_HEIGHT.toFloat() ) secondLineCurrentHeight = secondLineHeight thirdLineHeight = array.getDimension( R.styleable.PlayingView_thirdLineHeight, DEFAULT_LINE_HEIGHT.toFloat() ) thirdLineCurrentHeight = thirdLineHeight.times(0.3f) lineWidth = array.getDimension( R.styleable.PlayingView_lineWidth, DEFAULT_LINE_WIDTH.toFloat() ) lineColor = array.getColor(R.styleable.PlayingView_lineColor, Color.BLUE) array.recycle() initPaint() }fun startPlayingAnimation() { if (animatorSet == null) { animatorSet = AnimatorSet() val animatorFirst = ValueAnimator.ofFloat(0.3f, 1f, 0.3f) animatorFirst.duration = 1000 animatorFirst.repeatCount = ValueAnimator.INFINITE animatorFirst.addUpdateListener { animation -> firstLineCurrentHeight = (firstLineHeight * animation.animatedValue as Float) secondLineCurrentHeight = (secondLineHeight * animation.animatedValue as Float) thirdLineCurrentHeight = (thirdLineHeight * animation.animatedValue as Float) invalidate() } val animatorSecond = ValueAnimator.ofFloat(1f, 0.3f, 1f) animatorSecond.duration = 1000 animatorSecond.repeatCount = ValueAnimator.INFINITE animatorSecond.addUpdateListener { animation -> secondLineCurrentHeight = (secondLineHeight * animation.animatedValue as Float) } animatorSet!!.playTogether(animatorFirst, animatorSecond) } if (!animatorSet!!.isRunning) { animatorSet!!.start() } }fun setLineColor(color: Int) { mPaint.color = color }override fun onDraw(canvas: Canvas) { super.onDraw(canvas) drawFirstLine(canvas) drawSecondLine(canvas) drawThirdLine(canvas) }private fun drawFirstLine(canvas: Canvas) { canvas.drawLine( paddingLeft + lineWidth / 2, height - firstLineCurrentHeight, paddingLeft + lineWidth / 2, height - paddingBottom.toFloat(), mPaint ) }private fun drawSecondLine(canvas: Canvas) { //保证第二个肯定是在中间位置 需要考虑padding canvas.drawLine( paddingLeft + (width - paddingLeft - paddingRight) / 2.toFloat(), height - secondLineCurrentHeight, paddingLeft + (width - paddingLeft - paddingRight) / 2.toFloat(), height - paddingBottom.toFloat(), mPaint ) }private fun drawThirdLine(canvas: Canvas) { canvas.drawLine( width - paddingRight - (lineWidth / 2), height - thirdLineCurrentHeight, width - paddingRight - (lineWidth / 2), height - paddingBottom.toFloat(), mPaint ) }override fun onDetachedFromWindow() { super.onDetachedFromWindow() releaseAnimation() }override fun setVisibility(visibility: Int) { super.setVisibility(visibility) if (visibility == GONE || visibility == INVISIBLE) { releaseAnimation() } }private fun releaseAnimation() { if (animatorSet != null) { animatorSet!!.cancel() animatorSet = null } }override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec) val widthSize = MeasureSpec.getSize(widthMeasureSpec) val heightSpecMode = MeasureSpec.getMode(widthMeasureSpec) val heightSize = MeasureSpec.getSize(widthMeasureSpec) if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension( DEFAULT_WIDTH, DEFAULT_HEIGHT ) } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(DEFAULT_WIDTH, heightSize) } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSize, DEFAULT_HEIGHT) } } }

自定义属性:

布局中使用:

吐槽一下:android studio自带的格式化真的能逼疯我这强迫症~~

    推荐阅读