国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現粉碎的面具效果

2019-12-12 00:37:10
字體:
來源:轉載
供稿:網友

0.

首先話不多說,先上效果圖


這個gif把效果放慢了,真是運行時會快很多。

1.分析

看效果,咱們可以分析一下,整個效果有四種狀態,第一種就是普通狀態,第二種是抖動狀態,第三種是隱藏圖片和粉碎狀態,最后就是粉碎完成的狀態,這么一分析就很好搞了,根據不同的狀態來寫代碼。

2.普通狀態

首先是普通狀態,就是一個圖片的展示,這里我們可以看一下setImage方法

fun setImage(resId: Int){ image = BitmapFactory.decodeResource(context.resources, resId, null) preapreCircleColor() postInvalidate()}

可以看到image是一個bitmap,圖片來自drawable,這沒什么可說的,還有一個就是prepareCircleColor方法,這個方法是用來讀取bitmap不同位置的像素顏色,一次來確定粉碎時各個粒子的顏色。

private fun preapreCircleColor(){ image?.let { val step = it.width / Math.sqrt(circleNum.toDouble()) for (i in 0 until it.width step step.toInt()) {  for (j in 0 until it.height step step.toInt())  {  val color = it.getPixel(i, j)  if (circleAttributeList.size > 0)  {   circleAttributeList[i * 10 + j].color = color  }  } } }}

3.抖動狀態

抖動我們通過一個ValueAnimator來實現

private fun initShakingAnimator(){ shakingAnimator = ValueAnimator.ofInt(shakeCount) shakingAnimator.duration = shakeDuration.toLong() shakingAnimator.addListener(shakingListener) shakingAnimator.addUpdateListener { shakingNum = it.animatedValue as Int postInvalidate() }}

shakeCount代表了都動的次數,shakeDuration代表抖動的時間,這兩個屬性可以通過布局文件來配置。
在onDraw里可以看到drawShakingImage方法

private fun drawshakingImage(canvas: Canvas, centerX: Float, centerY: Float){ image?.let { var offset = 0 offset = if (offset == shakeCount) {  0 } else {  if (shakingNum % 2 == 0) shakeOffset else -shakeOffset } canvas.drawBitmap(image, centerX + offset - it.width / 2, centerY + offset - it.height / 2, paint) }}

方法很簡單,就是不停的繪制左右偏移的bitmap,當到達最大次數的時候偏移量為0。動畫結束后,將狀態位置為STATE.FADE

private val shakingListener = object : AnimatorListenerAdapter(){ override fun onAnimationEnd(animation: Animator?) { state = STATE.FADE fadeOutAnimator.start() bombAnimator.start() }}

3.隱藏粉碎狀態

動都結束后,就進入隱藏粉碎狀態了,這里我們用了兩個動畫,fadeOutAnimator和bombAnimator,fadeOutAnimator用來隱藏圖片,而bombAnimator則是用來繪制粉碎的粒子,關于圖片的隱藏就不說了,沒什么特別的,這里主要說說粉碎例子的繪制。

首先我們定義一個數據類

data class CircleAttribute(var startVerVelocity: Float, var horVelocity: Float,   var orX:Float,var orY:Float,   var x: Float, var y: Float, var color: Int,var radius:Float)

這個類用來表示每個粒子起始時豎直方向的速度,水平方向的速度,起始坐標,位置坐標,粒子顏色和半徑。
接著在onMeasure結束后,調用了一個方法prepareCircleAttributeList()

private fun prepareCircleAttributeList(){ circleAttributeList.clear() val centerX = measuredWidth.toFloat() / 2 val centerY = measuredHeight.toFloat() / 2 val maxVerVelocity = measuredHeight / bombDuration val maxHorVelocity = measuredWidth / 2 / bombDuration a = maxVerVelocity * 3 / bombDuration  for (i in 0 until circleNum) { var color = Color.WHITE val step = Math.sqrt(circleNum.toDouble()).toInt() var x = centerX var y = centerY image?.let {  val posXStep=it.width/step  val posYStep=it.height/step  val topX=centerX-it.width/2  val topY=centerY-it.height/2  val row = i / step  val col = i % step  color = it.getPixel(row * posXStep, col * posYStep)  x=topX+row*posXStep.toFloat()  y=topY+col*posYStep.toFloat() } val random = Math.random() val signal = (random * 4).toInt() val startVelocity = (Math.random() * maxVerVelocity).toFloat() val horVelocity = if (signal % 2 == 0) (Math.random() * maxHorVelocity).toFloat() else -(Math.random() * maxHorVelocity).toFloat() val attribute = CircleAttribute(startVelocity, horVelocity, x, y, x, y, color, (Math.random() * 15).toFloat()) circleAttributeList.add(attribute) }}

這個方法就是初始化每個粒子的數據的,最后將數據添加到circleAttributeList。其中a為豎直方向加速度,這里取得比較籠統,就是就是假定三分之一的粒子粉碎時間,最大速度就能減少到0。然后就是確定粒子的位置和顏色,粒子的數量是可以在布局文件控制的,粒子的位置和顏色基本上就是對bitmap的映射,所以如果有100個點,那么bitmap就可以看做10*10的一個粒子陣,每個粒子的位置和顏色是與其相對應的,理解了這個看代碼應該就明白了。

啟動動畫后,接下來就是位置的更新了,看initBombAnimator()方法

private fun initBombAnimator(){ bombAnimator = ValueAnimator.ofFloat(bombDuration) bombAnimator.duration = bombDuration.toLong() bombAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) {  super.onAnimationEnd(animation)  state = STATE.BOMBED  cancelAllAnimators()  bombFinishedListener?.onBombFinished()  circleAlpha = 0 } }) bombAnimator.addUpdateListener { val time = it.animatedValue as Float for (i in circleAttributeList) {  i.x = i.orX + i.horVelocity * time  i.y = i.orY - (i.startVerVelocity * time - 0.5f * a * time * time) } if (it.animatedFraction > 0.5) {  circleAlpha -= (0.5 * circleAlpha * it.animatedFraction).toInt() } postInvalidate() }}

水平方向的位置就是 i.x = i.orX + i.horVelocity * time, 標準的時間速度

豎直方向的位置就是 i.y = i.orY - (i.startVerVelocity * time - 0.5f * a * time * time) 公式s=v0t+1/2att,初中生都知道。circleAlpha是用來控制粒子的alpha值的。隨著動畫的進行,不停的進行invalidate,接下來看onDraw方法調用drawCircles方法

private fun drawCircles(canvas: Canvas){ for (i in circleAttributeList) { if (Color.alpha(i.color) == 0) {  paint.alpha = 0 } else {  paint.color = i.color  paint.alpha = circleAlpha } canvas.drawCircle(i.x, i.y, i.radius, paint) }}

這里有一點要注意的是,從bitmap里取到的顏色值是argb格式的,而paint設置的顏色是rgb格式的,所以如果取到的顏色alpha為0,將paint的alpha設置為0.最后動畫結束是將狀態位置為BOMBED,并調用回調函數

interface OnBombFinishedListener{ fun onBombFinished()}

4.總結

基本上原理就差不多是這些了,最后附上源碼地址

github (本地下載

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 恩平市| 临湘市| 天峨县| 泰州市| 攀枝花市| 滦南县| 云安县| 乐清市| 潮安县| 常宁市| 长武县| 清流县| 新绛县| 西丰县| 兴义市| 临高县| 监利县| 黎城县| 潮安县| 综艺| 丹棱县| 大连市| 云南省| 苏尼特左旗| 喜德县| 西乌珠穆沁旗| 精河县| 前郭尔| 阳新县| 西安市| 广水市| 吴川市| 佛学| 潜江市| 安阳市| 永新县| 镇坪县| 阿拉善左旗| 美姑县| 迁安市| 张掖市|