您现在的位置是:亿华云 > 数据库
HarmonyOS自定义UI之水波纹效果
亿华云2025-10-03 13:58:12【数据库】5人已围观
简介想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com概述 当点击一个view时,然后一个水波纹就会从点击处扩散开来,模拟水波背景动效实现。实
想了解更多内容,自之水请访问:
和华为官方合作共建的定义鸿蒙技术社区
https://harmonyos.51cto.com
概述
当点击一个view时,然后一个水波纹就会从点击处扩散开来,波纹模拟水波背景动效实现。效果
实现思路
任何东西都可以在生活中找到案例,自之水我们要做水波纹效果,定义就想象一下,波纹每个人应该都有把石头扔进进水里的效果经历,首先水波是自之水从中心点的小圆慢慢放大为大圆,然后慢慢消失,定义我们模拟的波纹时候只需要画圆,通过Canvas. drawCircle(float x,效果 float y, float radius, Paint paint); 方法来实现,通过一点点放大半径在根据变化速率设置透明度的自之水方式实现。
效果如下:

实现过程:
通过计算view的定义最大宽度 / 2为布局的最大半径,最小半径就是云南idc服务商波纹0然后给它一个插值动画让它动起来,每次点击就让它绘制圆,圆的半径为 (this.radiusMax * 插值动画比例 ),插值动画比例的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,代码如下:
//初始化画笔 private void inint() { this.animationRunning = false; this.paint = new Paint(); this.paint.setAntiAlias(true); this.paint.setStyle(FILLANDSTROKE_STYLE); this.paint.setColor(new Color(Color.getIntColor("#0000FF")));//设置水波纹的颜色 } @Override public void onDraw(Component component, Canvas canvas) { float touchX = (float) this.getWidth() / 2; float touchY = (float) this.getHeight() / 2; this.paint.setAlpha(1 - 1* this.ripplePose);//透明都也是从0到1 因为ripplePose 是从0-1变换 this.radiusMax = component.getWidth()/2; float radiusMax2 = component.getWidth()/4; //根据比例设置半径大小 canvas.drawCircle(touchX, touchY, this.radiusMax * this.ripplePose +radiusMax2, this.paint); }this.ripplePose的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,来绘制圆,这里我为什么加 radiusMax2是因为给他一个初始半径,让他不要从圆心开始,这样看起来就比较舒服了, 在点击的时候触发动画,代码如下:
@Override public boolean onTouchEvent(Component component, TouchEvent touchEvent) { if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_DOWN) { // 一个手指按下 this.downX = this.getTouchX(touchEvent, 0); this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == POINT_MOVE) { // 一个手指移动 } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_UP) { //一个手指抬起 this.invalidate(); } return false; }效果如下:
但是当我把水波纹应用到 PageSlider 中带有ListContainer 的云服务器时候,滑动的时候也会触发水波纹,所以需要解决事件冲突。
事件冲突解决
在添加了长按阴影效果后,在滑动PageSlider 页面的时候listContainer的子item也会触发点击事件,导致各种事件冲突,解决方法就是 在Touch事件中计算定义出各种事件,各个击破。
1.点击事件:抬起时间-按下时间 没有超过200ms
2.长按事件:按下超过200ms
3.长按滑动事件:移动监听里面计算 滑动距离超过20,时间超过200 没有抬起
4.短按滑动事件: 时间少于200ms 距离超过 200px 没有抬起
5.然后处理各种事件
另外发现,手机点击的时候移动事件会触发,而模拟器不会触发,这个也需要注意!
这样就完成了整个绘制过程,代码如下:
/** * 触摸监听 * * @param component view * @param touchEvent touchEvent * @return 是否消费 */ public boolean onTouchEvent(Component component, TouchEvent touchEvent) { if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM1) { this.downX = this.getTouchX(touchEvent, 0); this.downY = this.getTouchY(touchEvent, 0); this.downTime = System.currentTimeMillis(); this.isDownPose = true; this.isSlide = false; isYinYing = false; } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM3) { float upx = this.getTouchX(touchEvent, 0); long theTime = System.currentTimeMillis(); int xjuli = (int) Math.abs(upx - downX); int timejuli = (int) Math.abs(theTime - this.downTime); if (isDownPose && timejuli > NUM200 && xjuli < NUM2 && !isSlide) { this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); myevenHandler.sendEvent(1, NUM250); this.isSlide = true; return true; } return true; } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM2) { float upx = this.getTouchX(touchEvent, 0); long upTimes = System.currentTimeMillis(); isDownPose = false; isYinYing = false; int xjuli = (int) Math.abs(upx - downX); if (!this.isDownPose && upTimes - this.downTime < NUM200 && xjuli < NUM5) { this.isDownPose = false; this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); } else if (!this.isDownPose && System.currentTimeMillis() - this.downTime > NUM250) { this.invalidate(); } else { this.isDownPose = false; } this.isDownPose = false; } return true; } /** * 长按监听 * * @param component view */ @Override public void onLongClicked(Component component) { this.createAnimation(this.downX, this.downY); myevenHandler.sendEvent(1, NUM250); this.isSlide = true; } /** * 绘制 * * @param var1 Component * @param var2 Canvas */ public void onDraw(Component var1, Canvas var2) { if (isYinYing) { this.paint.setAlpha(NUM03F); var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - 0 / NUM2F, this.paint); } else { if (this.getWidth() != 0 && this.getHeight() != 0) { this.radiusMax = (float) Math.sqrt(this.getWidth() * this.getWidth() + this.getHeight() * this.getHeight()); if (this.rippleType != NUM2) { this.radiusMax /= NUM2F; } this.radiusMax -= (float) this.ripplePadding; if (this.isCentered || this.rippleType == 1) { this.touchX = (float) this.getWidth() / NUM2F; this.touchY = (float) this.getHeight() / NUM2F; } this.paint.setAlpha(this.rippleAlpha - this.rippleAlpha * this.ripplePose); float var3 = 0.0F; if (this.rippleType == NUM1 && this.ripplePose > NUM04) { this.paint.setStyle(Paint.Style.STROKE_STYLE); var3 = this.radiusMax * this.ripplePose - this.radiusMax * (this.ripplePose - NUM04) / NUM06; this.paint.setStrokeWidth(this.radiusMax * this.ripplePose - this.radiusMax * (this.ripplePose - NUM04) / NUM06); } else { this.paint.setStyle(Paint.Style.FILL_STYLE); } var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - var3 / NUM2F, this.paint); } } }效果如下:
完整项目代码
想了解更多内容,请访问:
和华为官方合作共建的源码下载鸿蒙技术社区
https://harmonyos.51cto.com
很赞哦!(8897)
下一篇: 2023年五大数据中心安全风险