ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-11-11 12:33:41  阅读:180  来源: 互联网

标签:自定义 val toFloat mBufferCanvas yOffset paint 0f Android 自习室


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

效果图如下:
在这里插入图片描述

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

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

首先声明3个变量:

private lateinit var mBufferBitmap: Bitmap
private lateinit var mBufferCanvas: Canvas
private 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轴的变量,即可实现"动画"了。动手试试吧(´・ω・`)

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

在这里插入图片描述

标签:自定义,val,toFloat,mBufferCanvas,yOffset,paint,0f,Android,自习室
来源: https://www.cnblogs.com/anyrtc/p/15539244.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有