Android自定义View实现粉碎的面具效果

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 (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 超酷炫的Android碎纸机效果推荐

    在Android开发中,有时候可能会要用到碎纸机的效果,今天小编为大家整理好代码,一起来看看吧. 首先来看下效果图 实例代码 xml <com.ldoublem.PaperShredderlib.PaperShredderView android:layout_width="200dp" android:id="@+id/ps_delete2" android:layout_height="220dp" paper:sherderBgCol

  • Android Fragment(动态,静态)碎片详解及总结

    Android Fragment(动态,静态)碎片详解 一.Fragment的相关概念(一)Fragment的基础知识 Fragment是Android3.0新增的概念,中文意思是碎片,它与Activity十分相似,用来在一个 Activity中描述一些行为或一部分用户界面.使用多个Fragment可以在一个单独的Activity中建 立多个UI面板,也可以在多个Activity中使用Fragment. Fragment拥有自己的生命 周期和接收.处理用户的事件,这样就不必在Activity写一

  • 5分钟快速实现Android爆炸破碎酷炫动画特效的示例

    这个破碎动画,是一种类似小米系统删除应用时的爆炸破碎效果的动画. 效果图展示 先来看下是怎样的动效,要是感觉不是理想的学习目标,就跳过,避免浪费大家的时间.�� 源码在这里:point_right: https://github.com/ReadyShowShow/explosion 一行代码即可调用该动画 new ExplosionField(this).explode(view, null)) 下面开始我们酷炫的Android动画特效正式讲解:point_down: 先来个整体结构的把握 整

  • Android 自定义View的使用介绍

    在项目开发中,可能系统自带的一些widget不能满足我们的需求,这时就需要自定义View. 通过查看系统中的常用widget如Button,TextView,EditText,他们都继承自View,所以我们在继承自定义View的时候也自然的需要继承View.1.首先新建一个类LView继承自View 复制代码 代码如下: public class LView extends View { private Paint paint; public LView(Context context) {  

  • Android自定义View实现粉碎的面具效果

    0. 首先话不多说,先上效果图 这个gif把效果放慢了,真是运行时会快很多. 1.分析 看效果,咱们可以分析一下,整个效果有四种状态,第一种就是普通状态,第二种是抖动状态,第三种是隐藏图片和粉碎状态,最后就是粉碎完成的状态,这么一分析就很好搞了,根据不同的状态来写代码. 2.普通状态 首先是普通状态,就是一个图片的展示,这里我们可以看一下setImage方法 fun setImage(resId: Int) { image = BitmapFactory.decodeResource(conte

  • Android自定义View 实现水波纹动画引导效果

    一.实现效果图 二.实现代码 1.自定义view package com.czhappy.showintroduce.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Pat

  • Android 自定义view实现水波纹动画效果

    在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了个让人兴奋的效果,兴致高昂的来找你,看了之后目的很明确,当然就是希望你能给她: 在这样的关键时候,身子板就一定得硬了,可千万别说不行,爷们儿怎么能说不行呢: 好了,为了让大家都能给妹纸们想要的,后面会逐渐分享一些比较比较不错的效果,目的只有一个,通过自定义view实现我们所能实现的动效: 今天主要分享水波纹效果: 1.标准正余弦水波纹: 2.非标准圆形液柱水波纹: 虽说都是水波纹,但两者在实现上差异是比较大的,一个

  • Android自定义View控件实现刷新效果

    三种得到LinearInflater的方法 a. LayoutInflater inflater = getLayoutInflater(); b. LayoutInflater localinflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); c. LayoutInflater inflater = LayoutInflater.from(context); onDraw 方法

  • Android 自定义View实现芝麻分曲线图效果

    1.简介 其实这个效果几天之前就写了,但是一直没有更新博客,本来想着把芝麻分雷达图也做好再发博客的,然后今天看到鸿洋的微信公众号有朋友发了芝麻分的雷达图,所以就算了,算是一个互补吧.平时文章也写的比较少,所以可能有点杂乱,有什么需要改进的地方欢迎给出建议,不胜感激. 效果图: 2.步骤: 初始化View的属性 初始化画笔 绘制代表最高分和最低分的两根虚线 绘制文字 绘制代表月份的属性 绘制芝麻分折线 绘制代表芝麻分的圆点 绘制选中分数的悬浮文字以及背景 处理点击事件 3.编码: 初始化View属

  • Android自定义view实现圆环进度条效果

    本文实例为大家分享了Android自定义view实现圆环进度条效果的具体代码,供大家参考,具体内容如下 一.实现效果图 二.核心代码 自定义view的属性 <?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="RingProgressBar">         <attr name="rin

  • Android自定义View实现水波纹扩散效果

    目录 1.创建RippleView.class, 继承与View 1.1特殊属性解释 1.2新建attrs.xml文件(res/values) 1.3初始化画笔 2.开始绘制onDraw() 效果:水波纹扩散 场景:雷达.按钮点击效果.搜索等 实现:先上效果图,之前记得支付宝有一个咻一咻,当时就是水波纹效果,实现起来一共两步,第一画内圆,第二画多个外圆,不同时创建有间隔创建然后缓慢增大外圆半径,到达最远距离时移除掉,扩散时把透明度从255-1不断赋值即可.复杂在第二步,开工. 开工 1.创建Ri

  • Android自定义view实现圆的扩散效果

    本文实例为大家分享了Android自定义View的实现水波纹,供大家参考,具体内容如下 一.实现效果 MainActivity.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.andro

  • Android自定义View实现竖向滑动回弹效果

    本文实例为大家分享了Android自定义View实现滑动回弹的具体代码,供大家参考,具体内容如下 前言 Android 页面滑动的时候的回弹效果 一.关键代码 public class UniversalBounceView extends FrameLayout implements IPull {       private static final String TAG = "UniversalBounceView";     //default.     private sta

  • Android自定义View实现简单水波纹效果

    本文实例为大家分享了Android自定义View实现水波纹效果的具体代码,供大家参考,具体内容如下 效果如下: 原理 控制代码 //这里用的kotlin //主线程刷新控件  val mHandler = object : Handler() {         override fun handleMessage(msg: Message?) {             waterRippleView.refreshView()         }      //开启动画,开线程,延时刷新pe

随机推荐