基于安卓平台的粒子破碎效果组件 Azexplosion, 实现了鸿蒙的功能化迁移和重构,代码已开源,欢迎各位开发者下载使用并提出宝贵意见!

https://gitee.com/isrc_ohos/azexplosion_ohos
Azexplosion_ohos 是一个实现粒子破碎动画效果的组件,用户可以通过点击手机屏幕上的破碎对象(一般是指手机屏幕上显示的图片或文字),来达到将该对象破碎的效果。
该组件可以设置手机屏幕上的对象是否具有破碎效果,同时也可以更换破碎对象和破碎对象的背景。
Azexplosion_ohos 组件视觉效果突出、使用方便、可扩展性强,与小米手机删除 APP 时的动态效果类似。
组件效果展示
组件应用仅包含一个主界面,在界面中存在图片和文字两种破碎对象。
当手指触碰图片(或文字)时,该图片(或文字)在视觉上呈现破碎效果,且破碎粒子的颜色与原图片(或文字)的颜色相对应。

图 1:破碎效果展示
Sample 解析
Azexplosion_ohos 组件的核心功能主要被封装在 Library 中,Sample 的功能实现很简洁,只需要构建整体的布局,并调用 Library 提供的监听接口为整体显示布局设置监听,即可实现效果执行的对象的破碎效果。
具体的实现步骤如下:
步骤 1:创建布局。
步骤 2:设置整体显示布局。
步骤 3:导入相关类并实例化对象。
步骤 4:为整体显示布局设置监听。
接下来我们来看一下每一个步骤涉及的详细操作。
①创建布局
首先在 XML 文件中创建一个 DirectionalLayout 的布局,宽度和高度都跟随父控件变化而调整。
<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:id="$+id:root"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical">
<Text
ohos:width="match_content" //文字破碎对象
ohos:text="破碎效果"
ohos:text_size="60vp"
ohos:top_margin="10vp"
ohos:left_margin="20vp"
ohos:bottom_margin="15vp"
ohos:right_padding="10vp"
ohos:left_padding="40vp"
ohos:height="match_content"/>
<DirectionalLayout ohos:id="$+id:group1"
ohos:width="match_parent"
ohos:height="100vp"
ohos:top_margin="10vp"
ohos:orientation="horizontal">
<Image //图片破碎对象
ohos:id="$+id:qq"
ohos:width="match_content"
ohos:height="match_content"
ohos:image_src="$media:qq"
ohos:left_margin="25vp"
ohos:right_margin="25vp"
ohos:top_margin="15vp"/>
......
②设置整体显示布局
为了显示的美观性,可以通过 setBackground 设置主界面的背景颜色:
//directionalLayout 指向步骤(1)中的布局
DirectionalLayout directionalLayout = (DirectionalLayout) LayoutScatter.getInstance(this).parse(ResourceTable.Layout_mian_activity, null, false);
ShapeElement element = new ShapeElement();
element.setRgbColor(new RgbColor(255,239,213));
directionalLayout.setBackground(element); //背景颜色设置
super.setUIContent(directionalLayout); //设置显示布局
在 MainAbility 中,通过 import 关键字导入 Library 中的 ExplosionField 类,并在 onStart() 方法中实例化 ExplosionField 类对象。
ExplosionField explosionField = new ExplosionField(this);
整体显示布局设置监听后,用户点击布局内某个组件,组件就会出现破碎现象。
explosionField.addListener((ComponentContainer)findComponentById(ResourceTable.Id_root));
Library 解析
ExplosionAnimator 主要用于生成粒子并执行粒子破碎动画,改变不同时刻的粒子状态;ExplosionField 主要功能负责粒子集的画布显示;Particle 类主要用于描述粒子的颜色、透明度等属性。

具体流程如图 3 所示:

此时,图片的 PixelMap 自带图片原本的像素信息,而由于文字得到的 PixelMap 是空的,所以默认显示黑色的破碎效果。
//为每一个布局内的组件添加点击监听
public void addListener(Component view) {
if (view instanceof ComponentContainer) {
ComponentContainer viewGroup = (ComponentContainer) view;
int count = viewGroup.getChildCount();
// 逐个取出布局内的破碎对象
for (int i = 0 ; i < count; i++) {
addListener(viewGroup.getComponentAt(i));
}
} else {
//为每一个破碎对象设置点击监听
view.setClickable(true);
view.setClickedListener(getOnClickListener());
}
}
//将每一个破碎对象转换为PixelMap
private PixelMap createBitmapFromView(Component view) {
//PixelMap参数初始化操作
PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
options.size = new Size(100,100);
//创建位图对象
PixelMap = PixelMap.create(options);
if(view.getName().equals("Id_qq")){
bitmap =getPixelMap(ResourceTable.Media_qq); //qq的PixelMap
}
if(view.getName().equals("Id_qzone"))
bitmap =getPixelMap(ResourceTable.Media_qzone); //qzone的PixelMap
if(view.getName().equals("Id_vx"))
bitmap =getPixelMap(ResourceTable.Media_vx); //微信的PixelMap
......
return bitmap; //将获取的PixelMap返回
}
接着调用 Particle 类的 generateParticle() 方法生成粒子:
//生成粒子
private Particle[][] generateParticles(PixelMap bitmap, Rect bound) {
int w = bound.getWidth(); //PixelMap的宽
int h = bound.getHeight(); // PixelMap的高
int partW_Count = w / Particle.PART_WH; //横向粒子个数
int partH_Count = h / Particle.PART_WH; //竖向粒子个数
//粒子的宽
int bitmap_part_w = bitmap.getImageInfo().size.width / partW_Count;
//粒子的高
int bitmap_part_h = bitmap.getImageInfo().size.height / partH_Count;
//粒子矩阵
Particle[][] particles = new Particle[partH_Count][partW_Count];
Point point = null;
for (int row = 0; row < partH_Count; row ++) { //行
for (int column = 0; column < partW_Count; column ++) { //列
//取得当前粒子所在位置的颜色
int color = bitmap.readPixel(new Position(column* bitmap_part_w, row * bitmap_part_h));
point = new Point(column, row); //x是列,y是行
particles[row][column] = Particle.generateParticle(color, bound, point);
}
}
return particles; //返回粒子矩阵
}
生成的破碎粒子如图 4 所示:

在创建 ExplosionAnimator 类对象的过程中,将被点击的破碎对象的 PixelMap 作为参数传入,得到 ExplosionAnimator 类对象的成员变量包含上述 PixelMap 生成的粒子集。
//创建元素为列表ExplosionAnimator类对象的数组列表
private ArrayList<ExplosionAnimator> explosionAnimators;
explosionAnimators = new ArrayList<ExplosionAnimator>();
//创建ExplosionAnimator 类对象
final ExplosionAnimator animator = new ExplosionAnimator(this, createBitmapFromView(view), rect);
//ExplosionAnimator 类对象添加到列表中
explosionAnimators.add(animator);
当监听器监听到屏幕被触碰时,通过(1)中创建的 ExplosionAnimator 类对象调用 start() 方法,通过 invalidate() 来刷新将要破碎的图片所对应的区块,invalidate() 方法会调用 onDraw() 方法进行动画绘制。
public void start() {
super.start();
mContainer.invalidate();
}
在 onDraw() 方法里,首先保存画布的绘制状态并修正因为状态栏导致的错位,然后循环调用 ExplosionAnimator 的 draw() 方法。
public void onDraw(Component component, Canvas canvas) {
canvas.save(); // 保存画布的绘制状态
canvas.translate(0,positions[1]); //修正因为状态栏导致的错位
for (ExplosionAnimator animator : explosionAnimators) {
animator.draw(canvas);
}
canvas.restore();
}
在 draw 方法中,每次绘制都调用一次 advance() 方法让粒子“前进一步”(逐渐向下扩散),然后设置画笔的新属性并重新绘制。
public void draw(Canvas canvas) {
//动画结束时停止
if(!isRunning()) {
return;
}
for (Particle[] particle : mParticles) {
for (Particle p : particle) {
p.advance(myvalue);
mPaint.setColor(new Color(p.color));
//只是这样设置,透明色会显示为黑色
mPaint.setAlpha((int) (p.alpha));
canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
}
}
mContainer.invalidate();
}
这样两者相互调用,不停地刷新,直到所有粒子都绘制完成,刷新停止,动画绘制流程如图 5 所示:

👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯

点“阅读原文”了解更多




