温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何实现自定义View之播放暂停控件

发布时间:2021-07-27 15:03:06 来源:亿速云 阅读:168 作者:小新 栏目:移动开发

这篇文章主要介绍如何实现自定义View之播放暂停控件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

首先看一下效果图:

如何实现自定义View之播放暂停控件

下面先分析一下原理:

如何实现自定义View之播放暂停控件

状态1是播放状态,有两个小矩形,外面是一个圆,它需要最终变换成状态3的暂停状态
状态2是两个小矩形变成如图的黑色三角的一个过程
我们可以通过动画来实现它,两个小矩形分别变成三角形的一半
同时再给画布一个90度的旋转

具体实现:

1.继承View

class PlayPauseView : View

2.重写构造函数

constructor(context: Context?) : this(context,null) constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs,0) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr){  init(context!!,attrs!!) }

一般的写法都是讲初始化代码放在三个参数的构造函数里,其它两个构造函数分别继承自参数更多的一个本类的构造函数,所以这里用的是this关键字

3.初始化参数

首先我们需要先在values包的attrs文件中先声明属性

<declare-styleable name="PlayPauseView">  <attr name="barWidth" format="dimension"/>  <attr name="barHeight" format="dimension"/>  <attr name="barPadding" format="dimension"/>  <attr name="barColor" format="color"/>  <attr name="barBgColor" format="color"/>  <attr name="barClockWise" format="boolean"/>  <attr name="barPlayingState" format="boolean"/> </declare-styleable>

然后在构造函数中拿到这些参数

mBarWidth = typedArray.getDimension(R.styleable.PlayPauseView_barWidth,10 * getDensity()) mBarHeight = typedArray.getDimension(R.styleable.PlayPauseView_barHeight,30 * getDensity()) mPadding = typedArray.getDimension(R.styleable.PlayPauseView_barPadding,10 * getDensity()) //可以通过上面的三个参数计算出下面的参数值,所以不再通过xml设置 mBarSpace = mBarHeight - mBarWidth * 2 mRadius = mBarWidth + mBarSpace.div(2) + mPadding mWidth = mRadius * 2 mWidth = mRadius * 2

mBarWidth 是小矩形的宽度,mBarHeight 是小矩形的高度,mPadding 是小矩形距离整个view的边界距离(参考上图中状态1中左边小矩形距离大矩形的距离,距离top和left应该是一样的,这个值就是mPadding )。

mBarSpace 是两个小矩形之间的距离,mRadius 是状态1中圆的半径,mWidth 、mWidth 是状态1中大矩形的宽高。(这些参数都是通过上面三个参数计算出来的)

同样的在初始化这一步,初始化画笔和两个小矩形(半三角)Path

mBgPaint = Paint(Paint.ANTI_ALIAS_FLAG) mBgPaint!!.color = mBgColor mBgPaint!!.style = Paint.Style.FILL mBarPaint = Paint(Paint.ANTI_ALIAS_FLAG) mBarPaint!!.color = mBarColor mBarPaint!!.style = Paint.Style.FILL mLeftPath = Path() mRightPath = Path()

同时通过动画使矩形变成三角的参数 mProgress,在onDraw中会用到

4.测量控件

在onMeasure方法中测量控件的宽高,主要是在xml中wrap_content或者具体数值的时候

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {  super.onMeasure(widthMeasureSpec, heightMeasureSpec)  val widthMode = MeasureSpec.getMode(widthMeasureSpec)  val heightMode = MeasureSpec.getMode(heightMeasureSpec)  val measureWidth = MeasureSpec.getSize(widthMeasureSpec)  val measureHeight = MeasureSpec.getSize(heightMeasureSpec)  when(widthMode){   MeasureSpec.EXACTLY ->{   mWidth = Math.min(measureWidth,measureHeight).toFloat()   mHeight = Math.min(measureWidth,measureHeight).toFloat()   setMeasuredDimension(mWidth.toInt(),mHeight.toInt())   }   MeasureSpec.AT_MOST -> {   mWidth = mRadius * 2   mHeight = mRadius * 2   setMeasuredDimension(mWidth.toInt(),mHeight.toInt())   }   MeasureSpec.UNSPECIFIED -> {   }  }  }

5.绘制

override fun onDraw(canvas: Canvas?) {  super.onDraw(canvas)  //需要重新设置,否则画出来的图形会保留上一次的  mLeftPath!!.rewind()  mRightPath!!.rewind()  mRadius = mWidth.div(2)  //先画一个圆  canvas!!.drawCircle(mWidth.div(2),mHeight.div(2),mRadius,mBgPaint)  //核心代码  //顺时针  if(isClockWise){   mLeftPath!!.moveTo(mPadding + (mBarWidth + mBarSpace.div(2)) * mProgress ,mPadding)   mLeftPath!!.lineTo(mPadding ,mPadding + mBarHeight)   mLeftPath!!.lineTo(mPadding + mBarWidth + mBarSpace.div(2) * mProgress,mPadding + mBarHeight)   mLeftPath!!.lineTo(mPadding + mBarWidth + mBarSpace.div(2) * mProgress,mPadding)   mLeftPath!!.close()   mRightPath!!.moveTo(mPadding + mBarWidth + mBarSpace - mBarSpace.div(2) * mProgress,mPadding)   mRightPath!!.lineTo(mPadding + mBarWidth + mBarSpace - mBarSpace.div(2) * mProgress,mPadding + mBarHeight)   mRightPath!!.lineTo(mPadding + mBarWidth * 2 + mBarSpace ,mPadding + mBarHeight)   mRightPath!!.lineTo(mPadding + mBarWidth * 2 + mBarSpace - (mBarWidth + mBarSpace.div(2)) * mProgress,mPadding)   mRightPath!!.close()  }  //逆时针  else{   mLeftPath!!.moveTo(mPadding,mPadding)   mLeftPath!!.lineTo(mPadding + (mBarWidth + mBarSpace.div(2)) * mProgress,mPadding + mBarHeight)   mLeftPath!!.lineTo(mPadding + mBarWidth + mBarSpace.div(2) * mProgress,mPadding + mBarHeight)   mLeftPath!!.lineTo(mPadding + mBarWidth + mBarSpace.div(2) * mProgress,mPadding)   mLeftPath!!.close()   mRightPath!!.moveTo(mPadding + mBarWidth + mBarSpace - mBarSpace.div(2) * mProgress,mPadding)   mRightPath!!.lineTo(mPadding + mBarWidth + mBarSpace - mBarSpace.div(2) * mProgress,mPadding + mBarHeight)   mRightPath!!.lineTo(mPadding + mBarWidth * 2 + mBarSpace - (mBarWidth + mBarSpace.div(2)) * mProgress,mPadding + mBarHeight)   mRightPath!!.lineTo(mPadding + mBarWidth * 2 + mBarSpace,mPadding)   mRightPath!!.close()  }  var corner = 0  if(isClockWise){   corner = 90  }else{   corner = -90  }  val rotation = corner * mProgress  //旋转画布  canvas.rotate(rotation,mWidth.div(2),mHeight.div(2))  canvas.drawPath(mLeftPath!!,mBarPaint)  canvas.drawPath(mRightPath!!,mBarPaint)  }

如何实现自定义View之播放暂停控件

通过这张图来看一下核心代码(顺时针)
A点的坐标(mPadding + (mBarWidth + mBarSpace.div(2)) * mProgress ,mPadding)
mPadding 是小矩形距离大矩形的距离,A点最终会到F点,两者相差一个矩形 + 两个矩形间隔/2的距离(就是 mBarWidth + mBarSpace.div(2) 的距离),通过乘以一个从0到1的mProgress的变化即可
同理可得 D到F,B到E,C到E的变化坐标

右侧的矩形也是如此计算,如果是逆时针旋转,三角形是倒过来的,原理也是一样的

6.动画

上面提到过我们需要一个从0到1的mProgress的变化(从播放到暂停),或者需要一个从1到0的mProgress(从暂停到播放)

动画核心代码如下:

val valueAnimator = ValueAnimator.ofFloat(if (isPlaying) 1f else 0f, if (isPlaying) 0f else 1f) valueAnimator.duration = 200  valueAnimator.addUpdateListener {  mProgress = it.animatedValue as Float  invalidate()  }  return valueAnimator

mProgress 不断地变化,然后调用invalidate(),不断地调用onDraw()方法

7.监听

setOnClickListener {  if(isPlaying){  pause()  mPlayPauseListener!!.pause()  }else{  play()  mPlayPauseListener!!.play()  } } private fun play() {  getAnimator().cancel()  setPlaying(true)  getAnimator().start()  } private fun pause() {  getAnimator().cancel()  setPlaying(false)  getAnimator().start() }

mPlayPauseListener是对外提供的接口,可以在Activity中拿到播放或者暂停的状态,以供我们下一步的操作

8.使用

最后附上这个自定义View目前有的属性:

app:barHeight="30dp"//矩形条的宽度 app:barWidth="10dp"//矩形条的高度 app:barPadding="20dp"//矩形条距离原点(边界)的距离 app:barClockWise="true"//是否是顺时针转动 app:barPlayingState="false"//默认的状态,播放或者暂停 app:barBgColor="@color/colorRed"//控件背景色 app:barColor="@color/black"//按钮颜色

在Activity或者Fragment中的使用:

 val playPauseView = findViewById<PlayPauseView>(R.id.play_pause_view)  //控件的点击事件  playPauseView.setPlayPauseListener(this)  //需要实现的方法  override fun play() {  Toast.makeText(this,"现在处于播放状态",Toast.LENGTH_SHORT).show()  }  override fun pause() {  Toast.makeText(this,"现在处于暂停状态",Toast.LENGTH_SHORT).show()  }

至此,这个自定义View大致上完成了,还有一些细节就不再这里细说了。如果你有兴趣深入了解,可以看一下这里:自定义View集合中的PlayPauseView,如果能随手点个Star也是极好的。

以上是“如何实现自定义View之播放暂停控件”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI