欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義View實現(xiàn)粉碎的面具效果

 更新時間:2018年08月03日 09:56:22   作者:滑板上的老砒霜  
這篇文章主要給大家介紹了關(guān)于Android自定義View實現(xiàn)粉碎的面具效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

0.

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


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

1.分析

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

2.普通狀態(tài)

首先是普通狀態(tài),就是一個圖片的展示,這里我們可以看一下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.抖動狀態(tài)

抖動我們通過一個ValueAnimator來實現(xiàn)

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

shakeCount代表了都動的次數(shù),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,當?shù)竭_最大次數(shù)的時候偏移量為0。動畫結(jié)束后,將狀態(tài)位置為STATE.FADE

private val shakingListener = object : AnimatorListenerAdapter()
{

 override fun onAnimationEnd(animation: Animator?)
 {
 state = STATE.FADE
 fadeOutAnimator.start()
 bombAnimator.start()
 }
}

3.隱藏粉碎狀態(tài)

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

首先我們定義一個數(shù)據(jù)類

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結(jié)束后,調(diào)用了一個方法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)
 }
}

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

啟動動畫后,接下來就是位置的更新了,看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方法調(diào)用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設(shè)置的顏色是rgb格式的,所以如果取到的顏色alpha為0,將paint的alpha設(shè)置為0.最后動畫結(jié)束是將狀態(tài)位置為BOMBED,并調(diào)用回調(diào)函數(shù)

interface OnBombFinishedListener
{
 fun onBombFinished()
}

4.總結(jié)

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

github (本地下載

總結(jié)

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

相關(guān)文章

最新評論