android 文字炸裂效果,Android 中 View 炸裂特效的实现分析 IT蓝豹

2023-10-19 15:40

本文主要是介绍android 文字炸裂效果,Android 中 View 炸裂特效的实现分析 IT蓝豹,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前几天微博上被一个很优秀的 Android 开源组件刷屏了 - ExplosionField,效果非常酷炫,有点类似 MIUI 卸载 APP 时的动画,先来感受一下。

explosionfield.gif

ExplosionField 不但效果很拉风,代码写得也相当好,让人忍不住要拿来好好读一下。

创建 ExplosionField

ExplosionField 继承自 View,在 onDraw 方法中绘制动画特效,并且它提供了一个 attach2Window 方法,可以把 ExplosionField 最为一个子 View 添加到 Activity 上的 root view 中。public static ExplosionField attach2Window(Activity activity) {

ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);

ExplosionField explosionField = new ExplosionField(activity);

rootView.addView(explosionField, new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));    return explosionField;

}1234567

explosionField 的 LayoutParams 属性都被设置为 MATCH_PARENT,

这样一来,一个 view 炸裂出来的粒子可以绘制在整个 Activity 所在的区域。知识点:可以用 Window.ID_ANDROID_CONTENT 来替代 android.R.id.content

炸裂之前的震动效果

在 View 的点击事件中,调用 mExplosionField.explode(v)之后,View 首先会震动,然后再炸裂。

震动效果比较简单,设定一个 [0, 1] 区间 ValueAnimator,然后在 AnimatorUpdateListener 的 onAnimationUpdate 中随机平移 x 和 y坐标,最后把 scale 和 alpha 值动态减为 0。int startDelay = 100;

ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

Random random = new Random();    @Override

public void onAnimationUpdate(ValueAnimator animation) {

view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f);

view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f);

}

});

animator.start();

view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).alpha(0f).start();123456789101112131415

根据 View 创建一个 bitmap

View 震动完了就开始进行最难的炸裂,并且炸裂是跟隐藏同时进行的,先来看一下炸裂的 API - void explode(Bitmap bitmap, Rect bound, long startDelay, long duration):

前两个参数 bitmap 和 bound 是关键,通过 View 来创建 bitmap 的代码比较有意思。

如果 View 是一个 ImageView,并且它的 Drawable 是一个 BitmapDrawable 就可以直接获取这个 Bitmap。if (view instanceof ImageView) {

Drawable drawable = ((ImageView) view).getDrawable();    if (drawable != null && drawable instanceof BitmapDrawable) {        return ((BitmapDrawable) drawable).getBitmap();

}

}123456

如果不是一个 ImageView,可以按照如下步骤创建一个 bitmap:新建一个 Canvas

根据 View 的大小创建一个空的 bitmap

把空的 bitmap 设置为 Canvas 的底布

把 view 绘制在 canvas上

把 canvas 的 bitmap 设置成 null

当然,绘制之前要清掉 View 的焦点,因为焦点可能会改变一个 View 的 UI 状态。

一下代码中用到的 sCanvas 是一个静态变量,这样可以节省每次创建时产生的开销。view.clearFocus();

Bitmap bitmap = createBitmapSafely(view.getWidth(),

view.getHeight(), Bitmap.Config.ARGB_8888, 1);if (bitmap != null) {    synchronized (sCanvas) {

Canvas canvas = sCanvas;

canvas.setBitmap(bitmap);

view.draw(canvas);

canvas.setBitmap(null);

}

}1234567891011

作者创建位图的办法非常巧妙,如果新建 Bitmap 时产生了 OOM,可以主动进行一次 GC - System.gc(),然后再次尝试创建。

这个函数的实现方式让人佩服作者的功力。public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {    try {        return Bitmap.createBitmap(width, height, config);

} catch (OutOfMemoryError e) {

e.printStackTrace();        if (retryCount > 0) {

System.gc();            return createBitmapSafely(width, height, config, retryCount - 1);

}        return null;

}

}123456789101112

出了 bitmap,还有一个一个很重要的参数 bound,它的创建相对比较简单:Rect r = new Rect();

view.getGlobalVisibleRect(r);int[] location = new int[2];

getLocationOnScreen(location);

r.offset(-location[0], -location[1]);

r.inset(-mExpandInset[0], -mExpandInset[1]);123456

首先获取 需要炸裂的View 的全局可视区域 - Rect r,然后通过 getLocationOnScreen(location) 获取 ExplosionField 在屏幕中的坐标,并根据这个坐标把 炸裂View 的可视区域进行平移,这样炸裂效果才会显示在 ExplosionField 中,最后根据 mExpandInset 值(默认为 0)扩展一下。

那创建的 bitmap 和 bound 有什么用呢?我们继续往下分析。

创建粒子

先来看一下炸裂成粒子这个方法的全貌:public void explode(Bitmap bitmap, Rect bound, long startDelay, long duration) {    final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap, bound);

explosion.addListener(new AnimatorListenerAdapter() {        @Override

public void onAnimationEnd(Animator animation) {

mExplosions.remove(animation);

}

});

explosion.setStartDelay(startDelay);

explosion.setDuration(duration);

mExplosions.add(explosion);

explosion.start();

}12345678910111213

这里要解释一下为什么用一个容器类变量 - mExplosions 来保存一个 ExplosionAnimator。因为 activity 中多个 View 的炸裂效果可能要同时进行,所以要把每个 View 对应的炸裂动画保存起来,等动画结束的时候再删掉。

作者自定义了一个继承自 ValueAnimator 的类 - ExplosionAnimator,它主要做了两件事情,一个是创建粒子 - generateParticle,另一个是绘制粒子 - draw(Canvas canvas)。

先来看一下构造函数:public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {

mPaint = new Paint();

mBound = new Rect(bound);    int partLen = 15;

mParticles = new Particle[partLen * partLen];

Random random = new Random(System.currentTimeMillis());    int w = bitmap.getWidth() / (partLen + 2);    int h = bitmap.getHeight() / (partLen + 2);    for (int i = 0; i 

mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random);

}

}

mContainer = container;

setFloatValues(0f, END_VALUE);

setInterpolator(DEFAULT_INTERPOLATOR);

setDuration(DEFAULT_DURATION);

}123456789101112131415161718

根据构造函数可以知道作者把 bitmap 分成了一个 17 x 17 的矩阵,每个元素的宽度和高度分别是 w 和 h。int w = bitmap.getWidth() / (partLen + 2);int h = bitmap.getHeight() / (partLen + 2);12

所有的粒子是一个 15 x 15 的矩阵,元素色值是位图对应的像素值。bitmap.getPixel((j + 1) * w, (i + 1) * h)1

结构如下图所示,其中空心部分是粒子。● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ●

● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●

generateParticle 会根据一定的算法随机地生成一个粒子。这部分比较繁琐,分析略去。

其中比较巧妙的还是它的 draw 方法:public boolean draw(Canvas canvas) {    if (!isStarted()) {        return false;

}    for (Particle particle : mParticles) {

particle.advance((float) getAnimatedValue());        if (particle.alpha > 0f) {

mPaint.setColor(particle.color);

mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));

canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);

}

}

mContainer.invalidate();    return true;

}123456789101112131415

刚开始我还一直比较困惑,既然绘制粒子是在 ExplosionField 的 onDraw 方法中进行,那肯定需要不停地刷新,结果作者并不是这么做的,实现方法又着实惊艳了一把。

首先,作者在 ExplosionAnimator 类中重载了 start() 方法,通过调用 mContainer.invalidate(mBound) 来刷新 将要炸裂的 View 所对应的区块。@Overridepublic void start() {    super.start();

mContainer.invalidate(mBound);

}12345

而 mContainer 即是占满了 activity 的 view - ExplosionField,它的 onDraw 方法中又会调用 ExplosionAnimator 的 draw 方法。@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    for (ExplosionAnimator explosion : mExplosions) {

explosion.draw(canvas);

}

}1234567

这样便形成了一个递归,两者相互调用,不停地刷新,直到所有粒子的 alpha 值变为 0,刷新就停下来了。public boolean draw(Canvas canvas) {    if (!isStarted()) {        return false;

}    for (Particle particle : mParticles) {

particle.advance((float) getAnimatedValue());        if (particle.alpha > 0f) {

mPaint.setColor(particle.color);

mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));

canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);

}

}

mContainer.invalidate();    return true;

}123456789101112131415

总结

这个开源库的代码质量相当高,十分佩服作者。

更多特效。。《IT蓝豹》

这篇关于android 文字炸裂效果,Android 中 View 炸裂特效的实现分析 IT蓝豹的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/240746

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P