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

首頁 > 系統(tǒng) > Android > 正文

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

2019-10-21 21:43:00
字體:
供稿:網(wǎng)友

0.

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

Android,View

這個(gè)gif把效果放慢了,真是運(yùn)行時(shí)會(huì)快很多。

1.分析

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

2.普通狀態(tài)

首先是普通狀態(tài),就是一個(gè)圖片的展示,這里我們可以看一下setImage方法

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

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

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.抖動(dòng)狀態(tài)

抖動(dòng)我們通過一個(gè)ValueAnimator來實(shí)現(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代表了都動(dòng)的次數(shù),shakeDuration代表抖動(dòng)的時(shí)間,這兩個(gè)屬性可以通過布局文件來配置。
在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,當(dāng)?shù)竭_(dá)最大次數(shù)的時(shí)候偏移量為0。動(dòng)畫結(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)

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

首先我們定義一個(gè)數(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)

這個(gè)類用來表示每個(gè)粒子起始時(shí)豎直方向的速度,水平方向的速度,起始坐標(biāo),位置坐標(biāo),粒子顏色和半徑。
接著在onMeasure結(jié)束后,調(diào)用了一個(gè)方法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) }}

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

啟動(dòng)動(dò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, 標(biāo)準(zhǔn)的時(shí)間速度

豎直方向的位置就是 i.y = i.orY - (i.startVerVelocity * time - 0.5f * a * time * time) 公式s=v0t+1/2att,初中生都知道。circleAlpha是用來控制粒子的alpha值的。隨著動(dòng)畫的進(jìn)行,不停的進(jìn)行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) }}

這里有一點(diǎn)要注意的是,從bitmap里取到的顏色值是argb格式的,而paint設(shè)置的顏色是rgb格式的,所以如果取到的顏色alpha為0,將paint的alpha設(shè)置為0.最后動(dòng)畫結(jié)束是將狀態(tài)位置為BOMBED,并調(diào)用回調(diào)函數(shù)

interface OnBombFinishedListener{ fun onBombFinished()}

4.總結(jié)

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

github

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿城市| 海门市| 手游| 班玛县| 盘山县| 盐山县| 陇西县| 德化县| 交城县| 永平县| 麦盖提县| 勐海县| 循化| 绩溪县| 高平市| 阳原县| 汉寿县| 松溪县| 林甸县| 沽源县| 同德县| 英超| 尼木县| 奎屯市| 沁源县| 行唐县| 西畴县| 汝南县| 彰化县| 新泰市| 竹北市| 长乐市| 湾仔区| 治县。| 舟曲县| 靖安县| 宁化县| 江源县| 淅川县| 晋城| 高青县|