写点什么

Android 技术分享| 【自习室】自定义 View 代替通知动画(2)

作者:anyRTC开发者
  • 2021 年 11 月 11 日
  • 本文字数:2987 字

    阅读完需:约 10 分钟

上篇文章我们完成了一条信息的测量和绘制,本篇我们来实现消息的平移动画


效果图如下:



在自定义 View 中,通常我比较喜欢额外创建一个 Bitmap 和一个 Canvas 来绘制动画效果。大家可以根据自己喜好修改,实现的方式有很多。


首先在首次测量的时候我们创建 Canvas、Matrix、Bitmap,如果你的实际使用场景中,View 的大小可能会更改,这里也可以每次测量都重新创建。


首先声明 3 个变量:


private lateinit var mBufferBitmap: Bitmapprivate lateinit var mBufferCanvas: Canvasprivate lateinit var mBufferMatrix: Matrix
复制代码


onMeasure中创建:


override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {  // 测量代码...                                                                                               if (!this::mBufferBitmap.isInitialized) {    mBufferBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)    mBufferCanvas = Canvas(mBufferBitmap)    mBufferMatrix = Matrix()  }}
复制代码


接下来我们修改一下消息数据模型,将每条消息的动画进度以及其他一些属性存储起来,以便后续拓展和修改。


data class Message(  val avatar: String,// 头像地址  val nickname: String,// 昵称  val joinRoom: Int,// 1=加入,否则均为退出,改为Boolean也可  var info: NicknameInfo,// 存储本条消息的宽高等信息  var shader: BitmapShader? = null,// 图片加载相关  var bitmap: Bitmap? = null,  var life: Int = 5,// 消息存活时间  val timing: Long = System.currentTimeMillis(),// 已消耗时间  var xProgress: Float = 1f,// x轴平移比例,取值1.0f~0.0f  var yProgress: Float = 0f,// y轴平移比例,同上)data class NicknameInfo(  val nickname: String,// 修改后的nickname(超过5个字符,后面变为省略号  val nicknameWidth: Float,// 昵称宽度  val messageWidth: Float,// 消息总宽  val statusTextWidth: Float// "加入房间"、"退出房间",这几个字的宽度。根据实际需求这里也可以改为全局变量,因为字宽基本上可以说是固定的)
复制代码


数据的命名和定义可以完全按照自己的喜好来,存储需要存储的数据即可。无论是自定义 View 还是其他,最终总是要落实到数据上的,定义好存储数据的结构和算法即可。


根据我们上面定义的数据,我们用 mBufferCanvas 和 mBufferMatrix 进行一个渲的染:p


private fun drawMessage() {  mBufferMatrix.reset()// 绘制前reset一下matrix,清空一下buffer bitmap。  // 这里使用 mBufferBitmap.eraseColor(Color.TRANSPARENT)也是可以的,至于两者有什么不同,我也不清楚(; ̄ェ ̄),欢迎留言补充  mBufferCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)                                                                                          val msg = messageList[0]// 上一篇文章中,我们使用的是一个全局变量,这里我偷偷换成了List,用其他形式也可以,因为需求中是显示两条消息,用数组也行,两个全局变量也可以,根据自己喜好即可  val info = msg.info  val xOffset = msg.xProgress * info.messageWidth  val yOffset = msg.yProgress * messageHeight + msg.yProgress * messagePadding  mBufferMatrix.setTranslate(xOffset, yOffset)// 平移效果通过matrix来实现,也可以直接在绘制的时候加上偏移量,我本来是这么做的,但创建的mBufferMatrix就显得很多余,我就改成用matrix了(´・ω・`)  mBufferCanvas.setMatrix(mBufferMatrix)// 记得设置上  drawMsg(// 绘制消息的代码我丢在一个方法里了,这样我绘制两条再调用一次drawMsg就行了,我可不想复制两遍代码(´・_・`)    msg,    info.messageWidth,    info.nicknameWidth,    info.nickname  )
post { invalidate() }// 防止子线程调用,进行一个post}
/** * DO NOT CALL THIS FUNCTION */private fun drawMsg( msg: Message, messageWidth: Float, nicknameWidth: Float, nickname: String) { path.reset()// 用前先reset,好习惯 paint.color = Color.parseColor("#F3F3F3")
val statusText = if (msg.joinRoom == 1) "进入直播间" else "退出直播间"//状态直接传进来也可,个人喜好 // 接下来你看到的所有xOffset、yOffset,都是我改用matrix之前的逻辑,把0f删掉、把x/yOffset取消注释,就变成不用matrix的版本了(´・ω・`) val messageLeft = measuredWidth - messageWidth// + xOffset path.addArc(// 给path添加一个半圆 messageLeft, //yOffset, 0f, messageLeft + avatarPadding + avatarHeight.toFloat(), /*yOffset*/0f + messageHeight.toFloat(), 90f, 180f ) // 给path添加矩形 path.moveTo(messageLeft + avatarHeight.shr(1).toFloat(), /*yOffset*/0f) path.lineTo(measuredWidth.toFloat(), /*yOffset*/0f) path.lineTo(measuredWidth.toFloat(), /*yOffset*/0f + messageHeight.toFloat()) path.lineTo( messageLeft + avatarHeight.shr(1).toFloat(), /*yOffset*/ 0f + messageHeight.toFloat() )
paint.color = Color.parseColor("#434343")// 背景色 mBufferCanvas.drawPath(path, paint)// 填充
// 绘制文本 paint.color = Color.WHITE mBufferCanvas.drawText( statusText, messageLeft + avatarHeight + avatarPadding.shl(1) + nicknameWidth + messagePadding, //(measuredWidth - statusTextWidth - statusTextPadding) + /*xOffset*/上面提到放开offset的算法,这里的注视就没删除,留下做参考0f, messageHeight.shr(1) + fontCenterOffset + /*yOffset*/0f, paint ) paint.color = Color.parseColor("#BCBCBC")// 绘制昵称 mBufferCanvas.drawText( nickname, messageLeft + avatarPadding.shl(1) + avatarHeight, //(messageWidth - statusTextWidth - statusTextPadding.shl(1) - nicknameWidth) + /*xOffset*/0f, messageHeight.shr(1) + fontCenterOffset + /*yOffset*/0f, paint ) msg.bitmap?.let {// 图片加载完成的话,绘制头像 mBufferCanvas.save() paint.shader = msg.shader val translateOffset = (messageHeight - it.width).shr(1) mBufferCanvas.translate( messageLeft + translateOffset, /*yOffset*/0f + translateOffset.toFloat() ) mBufferCanvas.drawCircle( it.width.shr(1).toFloat(), it.width.shr(1).toFloat()/*messageHeight.shr(1).toFloat()*/, avatarHeight.shr(1).toFloat(), paint ) paint.shader = null mBufferCanvas.restore() }}
复制代码


完成上面的代码后只需要修改 x 轴和 y 轴的变量,即可实现"动画"了。动手试试吧(´・ω・`)


下篇文章我们来实现添加消息、计时、生命结束后删除消息等功能,还有真正的动画效。



发布于: 2021 年 11 月 11 日阅读数: 6
用户头像

实时交互,万物互联! 2020.08.10 加入

实时交互,万物互联,全球实时互动云服务商领跑者!

评论

发布
暂无评论
Android技术分享| 【自习室】自定义View代替通知动画(2)